189 lines
7.4 KiB
Python
189 lines
7.4 KiB
Python
from odoo import api, fields, models, _
|
|
from odoo.exceptions import UserError, ValidationError
|
|
|
|
|
|
class ProjectProject(models.Model):
|
|
_inherit = 'project.project'
|
|
|
|
sequence_name = fields.Char("Project Number", copy=False, readonly=True)
|
|
task_sequence_id = fields.Many2one(
|
|
'ir.sequence',
|
|
string="Task Sequence",
|
|
readonly=True,
|
|
copy=False,
|
|
help="Sequence for tasks of this project"
|
|
)
|
|
discuss_channel_id = fields.Many2one(
|
|
'discuss.channel',
|
|
string="Channel",
|
|
domain="[('parent_channel_id', '=', default_projects_channel_id)]",
|
|
help="Select a channel for project communications. Channels must be sub-channels of the main Projects Channel."
|
|
)
|
|
default_projects_channel_id = fields.Many2one(
|
|
'discuss.channel',
|
|
default=lambda self: self._get_default_projects_channel(),
|
|
string="Default Projects Channel"
|
|
)
|
|
|
|
@api.model
|
|
def _get_default_projects_channel(self):
|
|
"""Get or create the default Projects Channel"""
|
|
channel = self.env['discuss.channel'].search([
|
|
('name', '=', 'Projects Channel'),
|
|
('channel_type', '=', 'channel')
|
|
], limit=1)
|
|
|
|
if not channel:
|
|
channel = self.env['discuss.channel'].create({
|
|
'name': 'Projects Channel',
|
|
'description': 'Main channel for all project communications',
|
|
'channel_type': 'channel',
|
|
})
|
|
return channel
|
|
|
|
def action_create_project_channel(self):
|
|
"""Create a new channel for this project under the Projects Channel"""
|
|
self.ensure_one()
|
|
|
|
if self.discuss_channel_id:
|
|
raise UserError(_("This project already has a channel assigned."))
|
|
|
|
# Create new channel
|
|
channel_vals = {
|
|
'name': self.name,
|
|
'description': _("Communication channel for project %s") % self.name,
|
|
'channel_type': 'channel',
|
|
'parent_channel_id': self.default_projects_channel_id.id,
|
|
}
|
|
|
|
new_channel = self.env['discuss.channel'].create(channel_vals)
|
|
self.discuss_channel_id = new_channel.id
|
|
|
|
# Add project members to the channel
|
|
self._add_project_members_to_channel()
|
|
|
|
return {
|
|
'type': 'ir.actions.act_window',
|
|
'res_model': 'discuss.channel',
|
|
'res_id': new_channel.id,
|
|
'view_mode': 'form',
|
|
'target': 'current',
|
|
'context': {'create': False}
|
|
}
|
|
|
|
def _add_project_members_to_channel(self):
|
|
"""Add all project members as followers of the channel"""
|
|
if not self.discuss_channel_id:
|
|
return
|
|
|
|
# Get all users related to this project
|
|
members_to_add = self.env['res.users']
|
|
|
|
# Add project members
|
|
if self.members_ids:
|
|
members_to_add |= self.members_ids
|
|
|
|
# Add project manager
|
|
if self.user_id:
|
|
members_to_add |= self.user_id
|
|
|
|
# Add project lead if exists
|
|
if hasattr(self, 'project_lead') and self.project_lead:
|
|
members_to_add |= self.project_lead
|
|
|
|
# Add members to channel
|
|
for member in members_to_add:
|
|
self.discuss_channel_id.add_members(member.partner_id.ids)
|
|
|
|
def write(self, vals):
|
|
"""Override write to update channel members when project members change"""
|
|
result = super().write(vals)
|
|
|
|
# If members changed, update channel members
|
|
if any(field in vals for field in ['members_ids', 'user_id', 'project_lead']):
|
|
for project in self:
|
|
if project.discuss_channel_id:
|
|
project._add_project_members_to_channel()
|
|
|
|
return result
|
|
|
|
|
|
@api.model
|
|
def _get_shared_project_sequence(self):
|
|
"""Get or create a shared sequence for all projects"""
|
|
sequence = self.env['ir.sequence'].sudo().search([('code', '=', 'project.project.sequence')], limit=1)
|
|
if not sequence:
|
|
sequence = self.env['ir.sequence'].sudo().create({
|
|
'name': _("Project Sequence"),
|
|
'implementation': 'no_gap',
|
|
'padding': 3,
|
|
'use_date_range': False,
|
|
'prefix': 'PROJ-',
|
|
'code': 'project.project.sequence',
|
|
})
|
|
return sequence
|
|
|
|
@api.model_create_multi
|
|
def create(self, vals_list):
|
|
projects = super().create(vals_list)
|
|
sequence = self._get_shared_project_sequence()
|
|
for project in projects:
|
|
if not project.sequence_name:
|
|
project.sequence_name = sequence.next_by_id()
|
|
if project.discuss_channel_id:
|
|
project._add_project_members_to_channel()
|
|
return projects
|
|
|
|
def _default_type_ids(self):
|
|
default_stage_ids = [
|
|
self.env.ref('project_task_timesheet_extended.task_type_backlog').id,
|
|
self.env.ref('project_task_timesheet_extended.task_type_development').id,
|
|
self.env.ref('project_task_timesheet_extended.task_type_code_review_and_merging').id,
|
|
self.env.ref('project_task_timesheet_extended.task_type_testing').id,
|
|
self.env.ref('project_task_timesheet_extended.task_type_deployment').id,
|
|
self.env.ref('project_task_timesheet_extended.task_type_completed').id,
|
|
]
|
|
|
|
# self.env.ref('project_task_timesheet_extended.task_type_cancelled').id,
|
|
# self.env.ref('project_task_timesheet_extended.task_type_hold').id,
|
|
return self.env['project.task.type'].browse(default_stage_ids)
|
|
|
|
project_lead = fields.Many2one("res.users", string="Project Lead")
|
|
members_ids = fields.Many2many('res.users', 'project_user_rel', 'project_id',
|
|
'user_id', 'Project Members', help="""Project's
|
|
members are users who can have an access to
|
|
the tasks related to this project."""
|
|
)
|
|
user_id = fields.Many2one('res.users', string='Project Manager', default=lambda self: self.env.user, tracking=True,
|
|
domain=lambda self: [('groups_id', 'in', [self.env.ref('project.group_project_manager').id,self.env.ref('project_task_timesheet_extended.group_project_supervisor').id]),('share','=',False)],)
|
|
|
|
type_ids = fields.Many2many(default=lambda self: self._default_type_ids())
|
|
|
|
|
|
estimated_hours = fields.Float(string="Estimated Hours")
|
|
task_estimated_hours = fields.Float(string="Task Estimated Hours", compute="_compute_task_estimated_hours", store=True)
|
|
actual_hours = fields.Float(string="Actual Hours", compute="_compute_actual_hours", store=True)
|
|
|
|
@api.depends('task_ids.estimated_hours')
|
|
def _compute_task_estimated_hours(self):
|
|
for project in self:
|
|
project.task_estimated_hours = sum(project.task_ids.mapped('estimated_hours'))
|
|
|
|
@api.depends('task_ids.timesheet_ids.unit_amount')
|
|
def _compute_actual_hours(self):
|
|
for project in self:
|
|
project.actual_hours = sum(project.task_ids.timesheet_ids.mapped('unit_amount'))
|
|
|
|
def add_users(self):
|
|
return {
|
|
'type': 'ir.actions.act_window',
|
|
'name': 'Add Users',
|
|
'res_model': 'project.user.assign.wizard',
|
|
'view_mode': 'form',
|
|
'view_id': self.env.ref('project_task_timesheet_extended.project_user_assignment_form_view').id,
|
|
'target': 'new',
|
|
'context': {'default_members_ids':[(6, 0, self.members_ids.ids)],
|
|
},
|
|
}
|
|
|