oca-workflow-process/odoo-bringout-oca-project-project_key/project_key/models/project_project.py

208 lines
6.7 KiB
Python

# 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()