1
0
forked from Mapan/odoo17e
odoo17e-kedaikipas58/addons/documents_project/models/project_project.py
2024-12-10 09:04:09 +07:00

234 lines
11 KiB
Python

# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import json
from collections import defaultdict
from odoo import api, fields, models, Command, _, _lt
from odoo.exceptions import AccessError, UserError
from odoo.models import PREFETCH_MAX
from odoo.tools import frozendict
class ProjectProject(models.Model):
_name = 'project.project'
_inherit = ['project.project', 'documents.mixin']
use_documents = fields.Boolean("Documents", default=True)
documents_folder_id = fields.Many2one('documents.folder', string="Workspace", domain="['|', ('company_id', '=', False), ('company_id', '=', company_id)]", copy=False,
help="Workspace in which all of the documents of this project will be categorized. All of the attachments of your tasks will be automatically added as documents in this workspace as well.")
documents_tag_ids = fields.Many2many('documents.tag', 'project_documents_tag_rel', string="Default Tags", domain="[('folder_id', 'parent_of', documents_folder_id)]", copy=True)
document_count = fields.Integer(compute='_compute_attached_document_count', string="Number of documents in Project", groups='documents.group_documents_user')
shared_document_ids = fields.One2many('documents.document', string='Shared Documents', compute='_compute_shared_document_ids')
shared_document_count = fields.Integer("Shared Documents Count", compute='_compute_shared_document_ids')
@api.constrains('documents_folder_id')
def _check_company_is_folder_company(self):
for project in self:
if project.documents_folder_id and project.documents_folder_id.company_id and project.company_id != project.documents_folder_id.company_id:
raise UserError(_('The "%s" workspace should either be in the "%s" company like this project or be open to all companies.', project.documents_folder_id.name, project.company_id.name))
def _compute_attached_document_count(self):
Task = self.env['project.task']
task_read_group = Task._read_group(
[('project_id', 'in', self.ids)],
['project_id'],
['id:array_agg'],
)
task_ids = []
task_ids_per_project_id = {}
for project, ids in task_read_group:
task_ids += ids
task_ids_per_project_id[project.id] = ids
Document = self.env['documents.document']
project_document_read_group = Document._read_group(
[('res_model', '=', 'project.project'), ('res_id', 'in', self.ids)],
['res_id'],
['__count'],
)
document_count_per_project_id = dict(project_document_read_group)
document_count_per_task_id = Task.browse(task_ids)._get_task_document_data()
for project in self:
task_ids = task_ids_per_project_id.get(project.id, [])
project.document_count = document_count_per_project_id.get(project.id, 0) \
+ sum(
document_count_per_task_id.get(task_id, 0)
for task_id in task_ids
)
def _compute_shared_document_ids(self):
tasks_read_group = self.env['project.task']._read_group(
[('project_id', 'in', self.ids)],
['project_id'],
['id:array_agg'],
)
project_id_per_task_id = {}
task_ids = []
for project, ids in tasks_read_group:
task_ids += ids
for task_id in ids:
project_id_per_task_id[task_id] = project.id
# perf optimization:
# prefer a subquery when searching documents on too many tasks
if len(task_ids) > PREFETCH_MAX:
task_ids = self.env['project.task']._search([('project_id', 'in', self.ids)])
documents_read_group = self.env['documents.document']._read_group(
[
'&',
('is_shared', '=', True),
'|',
'&',
('res_model', '=', 'project.project'),
('res_id', 'in', self.ids),
'&',
('res_model', '=', 'project.task'),
('res_id', 'in', task_ids),
],
['res_model', 'res_id'],
['id:array_agg'],
)
document_ids_per_project_id = defaultdict(list)
for res_model, res_id, ids in documents_read_group:
if res_model == 'project.project':
document_ids_per_project_id[res_id] += ids
else:
project_id = project_id_per_task_id[res_id]
document_ids_per_project_id[project_id] += ids
for project in self:
shared_document_ids = self.env['documents.document'] \
.browse(document_ids_per_project_id[project.id])
project.shared_document_ids = shared_document_ids
project.shared_document_count = len(shared_document_ids)
@api.onchange('documents_folder_id')
def _onchange_documents_folder_id(self):
self.env['documents.document'].search([
('res_model', '=', 'project.task'),
('res_id', 'in', self.task_ids.ids),
('folder_id', '=', self._origin.documents_folder_id.id),
]).folder_id = self.documents_folder_id
self.documents_tag_ids = False
def _create_missing_folders(self):
folders_to_create_vals = []
projects_with_folder_to_create = []
documents_project_folder_id = self.env.ref('documents_project.documents_project_folder').id
group_project_user_id = self.env.ref('project.group_project_user').id
for project in self:
if not project.documents_folder_id:
folder_vals = {
'name': project.name,
'parent_folder_id': documents_project_folder_id,
'company_id': project.company_id.id,
'group_ids': [Command.link(group_project_user_id)],
'read_group_ids': [Command.link(group_project_user_id)],
}
folders_to_create_vals.append(folder_vals)
projects_with_folder_to_create.append(project)
created_folders = self.env['documents.folder'].sudo().create(folders_to_create_vals)
for project, folder in zip(projects_with_folder_to_create, created_folders):
project.sudo().documents_folder_id = folder
@api.model_create_multi
def create(self, vals_list):
projects = super().create(vals_list)
if not self.env.context.get('no_create_folder'):
projects.filtered(lambda project: project.use_documents)._create_missing_folders()
return projects
def write(self, vals):
if 'company_id' in vals:
for project in self:
if project.documents_folder_id and project.documents_folder_id.company_id and len(project.documents_folder_id.project_ids) > 1:
other_projects = project.documents_folder_id.project_ids - self
if other_projects and other_projects.company_id.id != vals['company_id']:
lines = [f"- {project.name}" for project in other_projects]
raise UserError(_(
'You cannot change the company of this project, because its workspace is linked to the other following projects that are still in the "%s" company:\n%s\n\n'
'Please update the company of all projects so that they remain in the same company as their workspace, or leave the company of the "%s" workspace blank.',
other_projects.company_id.name, '\n'.join(lines), project.documents_folder_id.name))
if 'name' in vals and len(self.documents_folder_id.project_ids) == 1 and self.name == self.documents_folder_id.name:
self.documents_folder_id.sudo().name = vals['name']
res = super().write(vals)
if 'company_id' in vals:
for project in self:
if project.documents_folder_id and project.documents_folder_id.company_id:
project.documents_folder_id.company_id = project.company_id
if not self.env.context.get('no_create_folder'):
self.filtered('use_documents')._create_missing_folders()
return res
@api.returns('self', lambda value: value.id)
def copy(self, default=None):
# We have to add no_create_folder=True to the context, otherwise a folder
# will be automatically created during the call to create.
# However, we cannot use with_context, as it intanciates a new recordset,
# and this copy would call itself infinitely.
previous_context = self.env.context
self.env.context = frozendict(self.env.context, no_create_folder=True)
project = super().copy(default)
self.env.context = previous_context
if not self.env.context.get('no_create_folder') and project.use_documents and self.documents_folder_id:
project.documents_folder_id = self.documents_folder_id.copy({'name': project.name})
return project
def _get_stat_buttons(self):
buttons = super(ProjectProject, self)._get_stat_buttons()
if self.use_documents:
buttons.append({
'icon': 'file-text-o',
'text': _lt('Documents'),
'number': self.document_count,
'action_type': 'object',
'action': 'action_view_documents_project',
'additional_context': json.dumps({
'active_id': self.id,
}),
'show': self.use_documents,
'sequence': 20,
})
return buttons
def action_view_documents_project(self):
self.ensure_one()
return {
'res_model': 'documents.document',
'type': 'ir.actions.act_window',
'name': _("%(project_name)s's Documents", project_name=self.name),
'domain': [
'|',
'&',
('res_model', '=', 'project.project'), ('res_id', '=', self.id),
'&',
('res_model', '=', 'project.task'), ('res_id', 'in', self.tasks.ids),
],
'view_mode': 'kanban,tree,form',
'context': {'default_res_model': 'project.project', 'default_res_id': self.id, 'limit_folders_to_project': True, 'default_tag_ids': self.documents_tag_ids.ids},
}
def _get_document_tags(self):
return self.documents_tag_ids
def _get_document_folder(self):
return self.documents_folder_id
def _check_create_documents(self):
return self.use_documents and super()._check_create_documents()
def _check_project_read_access(self):
try:
self.check_access_rights('read')
self.check_access_rule('read')
except AccessError:
return False
return True