mirror of
https://github.com/bringout/oca-project.git
synced 2026-04-19 07:02:00 +02:00
Move 124 sale modules to oca-sale, create oca-project with 56 project modules from oca-workflow-process
🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
9eb7ae5807
commit
6094c218b2
2332 changed files with 125826 additions and 0 deletions
|
|
@ -0,0 +1,7 @@
|
|||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
|
||||
|
||||
from . import res_config_settings
|
||||
from . import res_company
|
||||
from . import project_role
|
||||
from . import project_assignment
|
||||
from . import project_project
|
||||
|
|
@ -0,0 +1,157 @@
|
|||
# Copyright 2018-2019 Brainbean Apps (https://brainbeanapps.com)
|
||||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
|
||||
|
||||
from odoo import _, api, fields, models
|
||||
from odoo.exceptions import ValidationError
|
||||
|
||||
|
||||
class ProjectAssignment(models.Model):
|
||||
_name = "project.assignment"
|
||||
_description = "Project Assignment"
|
||||
_inherit = ["mail.thread"]
|
||||
|
||||
active = fields.Boolean(
|
||||
default=True,
|
||||
)
|
||||
name = fields.Char(
|
||||
compute="_compute_name",
|
||||
store=True,
|
||||
index=True,
|
||||
)
|
||||
company_id = fields.Many2one(
|
||||
comodel_name="res.company",
|
||||
string="Company",
|
||||
default=lambda self: self.env.company,
|
||||
ondelete="cascade",
|
||||
)
|
||||
project_id = fields.Many2one(
|
||||
comodel_name="project.project",
|
||||
string="Project",
|
||||
ondelete="cascade",
|
||||
)
|
||||
role_id = fields.Many2one(
|
||||
comodel_name="project.role",
|
||||
string="Role",
|
||||
required=True,
|
||||
ondelete="restrict",
|
||||
)
|
||||
user_id = fields.Many2one(
|
||||
comodel_name="res.users",
|
||||
string="User",
|
||||
required=True,
|
||||
ondelete="restrict",
|
||||
)
|
||||
|
||||
_sql_constraints = [
|
||||
(
|
||||
"project_role_user_uniq",
|
||||
"UNIQUE (project_id, role_id, user_id)",
|
||||
"User may be assigned per role only once within a project!",
|
||||
),
|
||||
(
|
||||
"company_role_user_uniq",
|
||||
(
|
||||
"EXCLUDE ("
|
||||
" company_id WITH =, role_id WITH =, user_id WITH ="
|
||||
") WHERE ("
|
||||
" project_id IS NULL"
|
||||
")"
|
||||
),
|
||||
"User may be assigned per role only once within a company!",
|
||||
),
|
||||
(
|
||||
"nocompany_role_user_uniq",
|
||||
(
|
||||
"EXCLUDE (role_id WITH =, user_id WITH =) WHERE ("
|
||||
" project_id IS NULL AND company_id IS NULL"
|
||||
")"
|
||||
),
|
||||
"User may be assigned per role only once!",
|
||||
),
|
||||
]
|
||||
|
||||
@api.depends(
|
||||
"company_id.name",
|
||||
"project_id.name",
|
||||
"role_id.name",
|
||||
"user_id.name",
|
||||
)
|
||||
def _compute_name(self):
|
||||
for assignment in self:
|
||||
if assignment.project_id:
|
||||
assignment.name = _("%(USER)s as %(ROLE)s on %(PROJECT)s") % {
|
||||
"USER": assignment.user_id.name,
|
||||
"ROLE": assignment.role_id.name,
|
||||
"PROJECT": assignment.project_id.name,
|
||||
}
|
||||
elif assignment.company_id:
|
||||
assignment.name = _("%(USER)s as %(ROLE)s in %(PROJECT)s") % {
|
||||
"USER": assignment.user_id.name,
|
||||
"ROLE": assignment.role_id.name,
|
||||
"PROJECT": assignment.company_id.name,
|
||||
}
|
||||
else:
|
||||
assignment.name = _("%(USER)s as %(ROLE)s") % {
|
||||
"USER": assignment.user_id.name,
|
||||
"ROLE": assignment.role_id.name,
|
||||
}
|
||||
|
||||
def _get_conflicting_domain(self):
|
||||
self.ensure_one()
|
||||
return (
|
||||
[
|
||||
("id", "!=", self.id),
|
||||
("role_id", "=", self.role_id.id),
|
||||
("user_id", "=", self.user_id.id),
|
||||
]
|
||||
+ (
|
||||
[("company_id", "in", [False, self.company_id.id])]
|
||||
if self.company_id
|
||||
else []
|
||||
)
|
||||
+ (
|
||||
[("project_id", "in", [False, self.project_id.id])]
|
||||
if self.project_id
|
||||
else []
|
||||
)
|
||||
)
|
||||
|
||||
@api.constrains("company_id", "project_id", "role_id", "user_id")
|
||||
def _check(self):
|
||||
"""
|
||||
Check if assignment conflicts with any already-existing assignment and
|
||||
if specific role can be assigned at all (extension hook).
|
||||
"""
|
||||
for assignment in self:
|
||||
conflicting_assignment = self.search(
|
||||
assignment._get_conflicting_domain(),
|
||||
limit=1,
|
||||
)
|
||||
if conflicting_assignment:
|
||||
raise ValidationError(
|
||||
_(
|
||||
"Assignment %(ASSIGNMENT)s conflicts with another assignment: "
|
||||
"%(OTHER_ASSIGNMENT)s"
|
||||
)
|
||||
% {
|
||||
"ASSIGNMENT": assignment.name,
|
||||
"OTHER_ASSIGNMENT": conflicting_assignment.name,
|
||||
}
|
||||
)
|
||||
if not assignment.role_id.can_assign(
|
||||
assignment.user_id, assignment.project_id
|
||||
):
|
||||
if assignment.project_id:
|
||||
error = _(
|
||||
"User %(USER)s can not be assigned to role %(ROLE)s on %(PROJECT)s."
|
||||
) % {
|
||||
"USER": assignment.user_id.name,
|
||||
"ROLE": assignment.role_id.name,
|
||||
"PROJECT": assignment.project_id.name,
|
||||
}
|
||||
else:
|
||||
error = _("User %(USER)s can not be assigned to role %(ROLE)s.") % {
|
||||
"USER": assignment.user_id.name,
|
||||
"ROLE": assignment.role_id.name,
|
||||
}
|
||||
raise ValidationError(error)
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
# Copyright 2018-2019 Brainbean Apps (https://brainbeanapps.com)
|
||||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
|
||||
|
||||
from odoo import api, fields, models
|
||||
|
||||
|
||||
class ProjectProject(models.Model):
|
||||
_inherit = "project.project"
|
||||
|
||||
assignment_ids = fields.One2many(
|
||||
string="Project Assignments",
|
||||
comodel_name="project.assignment",
|
||||
inverse_name="project_id",
|
||||
tracking=True,
|
||||
)
|
||||
inherit_assignments = fields.Boolean(
|
||||
default=lambda self: self._default_inherit_assignments(),
|
||||
)
|
||||
limit_role_to_assignments = fields.Boolean(
|
||||
default=lambda self: self._default_limit_role_to_assignments(),
|
||||
)
|
||||
|
||||
@api.model
|
||||
def _default_inherit_assignments(self):
|
||||
company = self.env["res.company"].browse(
|
||||
self._context.get("company_id", self.env.company.id)
|
||||
)
|
||||
return company.project_inherit_assignments
|
||||
|
||||
@api.model
|
||||
def _default_limit_role_to_assignments(self):
|
||||
company = self.env["res.company"].browse(
|
||||
self._context.get("company_id", self.env.company.id)
|
||||
)
|
||||
return company.project_limit_role_to_assignments
|
||||
|
||||
def _project_role_create_assignment_values(self, vals_list):
|
||||
"""Complete values with default assignments from company"""
|
||||
company_ids = [v["company_id"] for v in vals_list if v.get("company_id")]
|
||||
companies = self.env["res.company"].browse(company_ids)
|
||||
for values in vals_list:
|
||||
company = None
|
||||
if values.get("company_id"):
|
||||
company = companies.filtered(lambda c: c.id == values["company_id"])
|
||||
if company and "inherit_assignments" not in values:
|
||||
values["inherit_assignments"] = company.project_inherit_assignments
|
||||
|
||||
if company and "limit_role_to_assignments" not in values:
|
||||
values[
|
||||
"limit_role_to_assignments"
|
||||
] = company.project_limit_role_to_assignments
|
||||
return vals_list
|
||||
|
||||
@api.model_create_multi
|
||||
def create(self, vals_list):
|
||||
vals_list = self._project_role_create_assignment_values(vals_list)
|
||||
return super().create(vals_list)
|
||||
|
|
@ -0,0 +1,135 @@
|
|||
# Copyright 2018-2019 Brainbean Apps (https://brainbeanapps.com)
|
||||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
|
||||
|
||||
from odoo import _, api, fields, models
|
||||
from odoo.exceptions import ValidationError
|
||||
from odoo.tools.translate import html_translate
|
||||
|
||||
|
||||
class ProjectRole(models.Model):
|
||||
_name = "project.role"
|
||||
_description = "Project Role"
|
||||
_parent_name = "parent_id"
|
||||
_parent_store = True
|
||||
_rec_name = "complete_name"
|
||||
_order = "complete_name"
|
||||
|
||||
active = fields.Boolean(
|
||||
default=True,
|
||||
)
|
||||
parent_path = fields.Char(index=True, unaccent=False)
|
||||
parent_id = fields.Many2one(
|
||||
string="Parent Role",
|
||||
comodel_name="project.role",
|
||||
index=True,
|
||||
ondelete="cascade",
|
||||
)
|
||||
child_ids = fields.One2many(
|
||||
string="Child Roles",
|
||||
comodel_name="project.role",
|
||||
inverse_name="parent_id",
|
||||
copy=True,
|
||||
)
|
||||
complete_name = fields.Char(
|
||||
compute="_compute_complete_name", store=True, recursive=True
|
||||
)
|
||||
name = fields.Char(
|
||||
translate=True,
|
||||
required=True,
|
||||
)
|
||||
description = fields.Html(
|
||||
translate=html_translate,
|
||||
)
|
||||
company_id = fields.Many2one(
|
||||
comodel_name="res.company",
|
||||
string="Company",
|
||||
default=lambda self: self.env.company,
|
||||
ondelete="cascade",
|
||||
)
|
||||
|
||||
_sql_constraints = [
|
||||
(
|
||||
"name_company_uniq",
|
||||
"UNIQUE (name, company_id)",
|
||||
"Role with such name already exists in the company!",
|
||||
),
|
||||
(
|
||||
"name_nocompany_uniq",
|
||||
("EXCLUDE (name WITH =) WHERE (" " company_id IS NULL" ")"),
|
||||
"Shared role with such name already exists!",
|
||||
),
|
||||
]
|
||||
|
||||
@api.constrains("name")
|
||||
def _check_name(self):
|
||||
for role in self:
|
||||
if self.search(
|
||||
[
|
||||
("company_id", "=" if role.company_id else "!=", False),
|
||||
("name", "=", role.name),
|
||||
],
|
||||
limit=1,
|
||||
):
|
||||
raise ValidationError(
|
||||
_('Role "%s" conflicts with another role due to same name.')
|
||||
% (role.name,)
|
||||
)
|
||||
|
||||
@api.depends("name", "parent_id.complete_name")
|
||||
def _compute_complete_name(self):
|
||||
for role in self:
|
||||
if role.parent_id:
|
||||
role.complete_name = _("%(parent)s / %(own)s") % {
|
||||
"parent": role.parent_id.complete_name,
|
||||
"own": role.name,
|
||||
}
|
||||
else:
|
||||
role.complete_name = role.name
|
||||
|
||||
@api.constrains("active")
|
||||
def _check_active(self):
|
||||
for role in self:
|
||||
if (
|
||||
role.active
|
||||
and role.parent_id
|
||||
and role.parent_id not in self
|
||||
and not role.parent_id.active
|
||||
):
|
||||
raise ValidationError(
|
||||
_("Please activate first parent role %s")
|
||||
% (role.parent_id.complete_name,)
|
||||
)
|
||||
|
||||
def can_assign(self, user_id, project_id):
|
||||
"""Extension point to check if user can be assigned to this role"""
|
||||
self.ensure_one()
|
||||
return self.active
|
||||
|
||||
@api.model
|
||||
def get_available_roles(self, user_id, project_id):
|
||||
"""
|
||||
Get domain on roles that can be assumed by given user on a specific
|
||||
project, depending on company and project assignments configuration.
|
||||
"""
|
||||
if not user_id or not project_id:
|
||||
return self
|
||||
|
||||
if not project_id.limit_role_to_assignments:
|
||||
if project_id.inherit_assignments:
|
||||
domain = [("company_id", "in", [False, user_id.company_id.id])]
|
||||
else:
|
||||
domain = [("company_id", "=", user_id.company_id.id)]
|
||||
return self.search(domain)
|
||||
|
||||
domain = [("user_id", "=", user_id.id)]
|
||||
if project_id.inherit_assignments:
|
||||
domain += [
|
||||
("project_id", "in", [False, project_id.id]),
|
||||
("company_id", "in", [False, user_id.company_id.id]),
|
||||
]
|
||||
else:
|
||||
domain += [
|
||||
("project_id", "=", project_id.id),
|
||||
("company_id", "=", user_id.company_id.id),
|
||||
]
|
||||
return self.env["project.assignment"].search(domain).mapped("role_id")
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
# Copyright 2019 Brainbean Apps (https://brainbeanapps.com)
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
|
||||
from odoo import fields, models
|
||||
|
||||
|
||||
class ResCompany(models.Model):
|
||||
_inherit = "res.company"
|
||||
|
||||
project_inherit_assignments = fields.Boolean(
|
||||
string="Projects Inherit Assignments",
|
||||
default=True,
|
||||
)
|
||||
project_limit_role_to_assignments = fields.Boolean(
|
||||
string="Limit Project Role to Assignments",
|
||||
default=False,
|
||||
)
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
# Copyright 2019 Brainbean Apps (https://brainbeanapps.com)
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
|
||||
from odoo import fields, models
|
||||
|
||||
|
||||
class ResConfigSettings(models.TransientModel):
|
||||
_inherit = "res.config.settings"
|
||||
|
||||
project_inherit_assignments = fields.Boolean(
|
||||
related="company_id.project_inherit_assignments",
|
||||
readonly=False,
|
||||
)
|
||||
project_limit_role_to_assignments = fields.Boolean(
|
||||
related="company_id.project_limit_role_to_assignments",
|
||||
readonly=False,
|
||||
)
|
||||
Loading…
Add table
Add a link
Reference in a new issue