mirror of
https://github.com/bringout/oca-project.git
synced 2026-04-18 21:22:09 +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,4 @@
|
|||
# License LGPLv3.0 or later (https://www.gnu.org/licenses/lgpl-3.0.en.html).
|
||||
|
||||
from . import project_project
|
||||
from . import project_task
|
||||
|
|
@ -0,0 +1,208 @@
|
|||
# Copyright 2017 - 2018 Modoolar <info@modoolar.com>
|
||||
# License LGPLv3.0 or later (https://www.gnu.org/licenses/lgpl-3.0.en.html).
|
||||
|
||||
from odoo import _, api, fields, models
|
||||
from odoo.tools import config
|
||||
|
||||
|
||||
class Project(models.Model):
|
||||
_inherit = "project.project"
|
||||
_rec_names_search = ["key", "name", "id"]
|
||||
|
||||
task_key_sequence_id = fields.Many2one(
|
||||
comodel_name="ir.sequence", string="Key Sequence", ondelete="restrict"
|
||||
)
|
||||
|
||||
key = fields.Char(size=10, required=False, index=True, copy=False)
|
||||
|
||||
_sql_constraints = [
|
||||
("project_key_unique", "UNIQUE(key)", "Project key must be unique")
|
||||
]
|
||||
|
||||
@api.onchange("name")
|
||||
def _onchange_project_name(self):
|
||||
for rec in self:
|
||||
if rec.key:
|
||||
continue
|
||||
|
||||
if rec.name:
|
||||
rec.key = self.generate_project_key(rec.name)
|
||||
else:
|
||||
rec.key = ""
|
||||
|
||||
@api.model_create_multi
|
||||
def create(self, vals_list):
|
||||
for vals in vals_list:
|
||||
key = vals.get("key", False)
|
||||
if not key:
|
||||
vals["key"] = self.generate_project_key(vals["name"])
|
||||
|
||||
# Tasks must be created after the project.
|
||||
if vals.get("task_ids", False):
|
||||
task_vals = vals.pop("task_ids")
|
||||
else:
|
||||
task_vals = []
|
||||
|
||||
# The key sequences to create stories and tasks with keys, created with
|
||||
# a project, must be linked to the project company to avoid security
|
||||
# issues.
|
||||
# Propagate the company ID, using the context key, to fill the
|
||||
# sequences company.
|
||||
company_id = vals.get("company_id")
|
||||
if company_id:
|
||||
self = self.with_context(project_sequence_company=company_id)
|
||||
|
||||
new_project = super(Project, self).create(vals)
|
||||
new_project.create_sequence()
|
||||
|
||||
# Tasks must be created after the project.
|
||||
if task_vals:
|
||||
new_project.write({"task_ids": task_vals})
|
||||
|
||||
return new_project
|
||||
|
||||
def write(self, values):
|
||||
update_key = False
|
||||
|
||||
if "key" in values:
|
||||
key = values["key"]
|
||||
update_key = self.key != key
|
||||
|
||||
res = super(Project, self).write(values)
|
||||
|
||||
if update_key:
|
||||
# Here we don't expect to have more than one record
|
||||
# because we can not have multiple projects with the same KEY.
|
||||
self.update_sequence()
|
||||
self._update_task_keys()
|
||||
|
||||
return res
|
||||
|
||||
def unlink(self):
|
||||
for project in self:
|
||||
sequence = project.task_key_sequence_id
|
||||
project.task_key_sequence_id = False
|
||||
sequence.sudo().unlink()
|
||||
return super(Project, self).unlink()
|
||||
|
||||
def create_sequence(self):
|
||||
"""
|
||||
This method creates ir.sequence fot the current project
|
||||
:return: Returns create sequence
|
||||
"""
|
||||
self.ensure_one()
|
||||
sequence_data = self._prepare_sequence_data()
|
||||
sequence = self.env["ir.sequence"].sudo().create(sequence_data)
|
||||
self.write({"task_key_sequence_id": sequence.id})
|
||||
return sequence
|
||||
|
||||
def update_sequence(self):
|
||||
"""
|
||||
This method updates existing task sequence
|
||||
:return:
|
||||
"""
|
||||
sequence_data = self._prepare_sequence_data(init=False)
|
||||
self.task_key_sequence_id.sudo().write(sequence_data)
|
||||
|
||||
def _prepare_sequence_data(self, init=True):
|
||||
"""
|
||||
This method prepares data for create/update_sequence methods
|
||||
:param init: Set to False in case you don't want to set initial values
|
||||
for number_increment and number_next_actual
|
||||
"""
|
||||
values = {
|
||||
"name": "{} {}".format(_("Project task sequence for project"), self.name),
|
||||
"implementation": "standard",
|
||||
"code": "project.task.key.{}".format(self.id),
|
||||
"prefix": "{}-".format(self.key),
|
||||
"use_date_range": False,
|
||||
}
|
||||
|
||||
# The key sequences to create stories and tasks with keys, created with
|
||||
# a project, must be linked to the project company to avoid security
|
||||
# issues.
|
||||
company_id = self.env.context.get("project_sequence_company")
|
||||
if company_id:
|
||||
values["company_id"] = company_id
|
||||
|
||||
if init:
|
||||
values.update(dict(number_increment=1, number_next_actual=1))
|
||||
|
||||
return values
|
||||
|
||||
def get_next_task_key(self):
|
||||
test_project_key = self.env.context.get("test_project_key")
|
||||
if (config["test_enable"] and not test_project_key) or (
|
||||
config["demo"].get("project_key") and not test_project_key
|
||||
):
|
||||
return False
|
||||
return self.sudo().task_key_sequence_id.next_by_id()
|
||||
|
||||
def generate_project_key(self, text):
|
||||
test_project_key = self.env.context.get("test_project_key")
|
||||
if (config["test_enable"] and not test_project_key) or (
|
||||
config["demo"].get("project_key") and not test_project_key
|
||||
):
|
||||
return False
|
||||
|
||||
if not text:
|
||||
return ""
|
||||
|
||||
data = text.split(" ")
|
||||
if len(data) == 1:
|
||||
return self._generate_project_unique_key(data[0][:3].upper())
|
||||
|
||||
key = []
|
||||
for item in data:
|
||||
key.append(item[:1].upper())
|
||||
return self._generate_project_unique_key("".join(key))
|
||||
|
||||
def _generate_project_unique_key(self, text):
|
||||
self_context = self.with_context(active_test=False)
|
||||
res = text
|
||||
unique_key = False
|
||||
counter = 0
|
||||
while not unique_key:
|
||||
if counter != 0:
|
||||
res = "%s%s" % (text, counter)
|
||||
unique_key = not bool(self_context.search([("key", "=", res)]))
|
||||
counter += 1
|
||||
|
||||
return res
|
||||
|
||||
def _update_task_keys(self):
|
||||
"""
|
||||
This method will update task keys of the current project.
|
||||
"""
|
||||
self.ensure_one()
|
||||
self.flush_model()
|
||||
reindex_query = """
|
||||
UPDATE project_task
|
||||
SET key = x.key
|
||||
FROM (
|
||||
SELECT t.id, p.key || '-' || split_part(t.key, '-', 2) AS key
|
||||
FROM project_task t
|
||||
INNER JOIN project_project p ON t.project_id = p.id
|
||||
WHERE t.project_id = %s
|
||||
) AS x
|
||||
WHERE project_task.id = x.id;
|
||||
"""
|
||||
|
||||
self.env.cr.execute(reindex_query, (self.id,))
|
||||
self.task_ids.invalidate_model(["key"])
|
||||
|
||||
@api.model
|
||||
def _set_default_project_key(self):
|
||||
"""
|
||||
This method will be called from the post_init hook in order to set
|
||||
default values on project.project and
|
||||
project.task, so we leave those tables nice and clean after module
|
||||
installation.
|
||||
:return:
|
||||
"""
|
||||
for project in self.search([("key", "=", False)]):
|
||||
project.key = self.generate_project_key(project.name)
|
||||
project.create_sequence()
|
||||
|
||||
for task in project.task_ids:
|
||||
task.key = project.get_next_task_key()
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
# Copyright 2017 - 2018 Modoolar <info@modoolar.com>
|
||||
# License LGPLv3.0 or later (https://www.gnu.org/licenses/lgpl-3.0.en.html).
|
||||
|
||||
from odoo import api, fields, models
|
||||
|
||||
TASK_URL = "/web#id=%s&view_type=form&model=project.task&action=%s"
|
||||
|
||||
|
||||
class Task(models.Model):
|
||||
_inherit = "project.task"
|
||||
_rec_names_search = ["key", "name"]
|
||||
|
||||
key = fields.Char(size=20, required=False, index=True)
|
||||
|
||||
url = fields.Char(string="URL", compute="_compute_task_url")
|
||||
|
||||
_sql_constraints = [("task_key_unique", "UNIQUE(key)", "Task key must be unique!")]
|
||||
|
||||
def _compute_task_url(self):
|
||||
action_id = self.env.ref("project.action_view_task").id
|
||||
for task in self:
|
||||
task.url = TASK_URL % (task.id, action_id)
|
||||
|
||||
@api.model_create_multi
|
||||
def create(self, vals_list):
|
||||
ctx = self.env.context.get
|
||||
for vals in vals_list:
|
||||
project_id = vals.get("project_id", False)
|
||||
if not project_id:
|
||||
project_id = ctx("default_project_id", False)
|
||||
|
||||
if not project_id and ctx("active_model", False) == "project.project":
|
||||
project_id = ctx("active_id", False)
|
||||
|
||||
if project_id:
|
||||
project = self.env["project.project"].browse(project_id)
|
||||
vals["key"] = project.get_next_task_key()
|
||||
return super(Task, self).create(vals_list)
|
||||
|
||||
def write(self, vals):
|
||||
project_id = vals.get("project_id", False)
|
||||
if not project_id:
|
||||
return super(Task, self).write(vals)
|
||||
|
||||
project = self.env["project.project"].browse(project_id)
|
||||
for task in self:
|
||||
if task.key and task.project_id.id == project.id:
|
||||
continue
|
||||
|
||||
values = self.prepare_task_for_project_switch(task, project)
|
||||
super(Task, task).write(values)
|
||||
|
||||
return super(Task, self).write(vals)
|
||||
|
||||
def prepare_task_for_project_switch(self, task, project):
|
||||
data = {"key": project.get_next_task_key(), "project_id": project.id}
|
||||
|
||||
if len(task.child_ids) > 0:
|
||||
data["child_ids"] = [
|
||||
(1, child.id, self.prepare_task_for_project_switch(child, project))
|
||||
for child in task.child_ids
|
||||
]
|
||||
return data
|
||||
|
||||
def name_get(self):
|
||||
result = []
|
||||
|
||||
for record in self:
|
||||
task_name = []
|
||||
if record.key:
|
||||
task_name.append(record.key)
|
||||
task_name.append(record.name)
|
||||
result.append((record.id, " - ".join(task_name)))
|
||||
|
||||
return result
|
||||
Loading…
Add table
Add a link
Reference in a new issue