[MIG] base_user_role_history: Migration to 17.0

This commit is contained in:
Christopher Rogos
2024-07-08 14:25:28 +00:00
parent 5bc11801a0
commit 6e671c0fb0
12 changed files with 297 additions and 152 deletions

View File

@@ -74,6 +74,8 @@ Contributors
- Benoit Aimont <benoit.aimont@acsone.eu> (https://acsone.eu)
- Thomas Binsfeld <thomas.binsfeld@acsone.eu> (https://acsone.eu)
- Christopher Rogos <crogos@gmail.com> (https://glueckkanja.com)
- Mohamed Osman <mohamed.osman3@yahoo.com> (https://glueckkanja.com)
Maintainers
-----------

View File

@@ -5,7 +5,7 @@
"name": "Base User Role History",
"summary": """
This module allows to track the changes on users roles.""",
"version": "16.0.1.0.0",
"version": "17.0.1.0.0",
"license": "AGPL-3",
"author": "ACSONE SA/NV, " "Odoo Community Association (OCA)",
"website": "https://github.com/OCA/server-backend",

View File

@@ -1,2 +1,3 @@
from . import res_users
from . import base_user_role_line_history
from . import res_users_role_line

View File

@@ -1,7 +1,7 @@
# Copyright 2019 ACSONE SA/NV
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo import api, fields, models
from odoo import fields, models
class BaseUserRoleLineHistory(models.Model):
@@ -13,105 +13,28 @@ class BaseUserRoleLineHistory(models.Model):
string="Action",
selection=[("add", "Add"), ("unlink", "Delete"), ("edit", "Edit")],
required=True,
readonly=True,
)
user_id = fields.Many2one(
string="User",
comodel_name="res.users",
ondelete="cascade",
readonly=True,
index=True,
)
old_role_id = fields.Many2one(
string="Old role",
comodel_name="res.users.role",
ondelete="cascade",
readonly=True,
index=True,
)
new_role_id = fields.Many2one(
string="New role",
comodel_name="res.users.role",
ondelete="cascade",
readonly=True,
index=True,
)
old_date_from = fields.Date(string="Old start date", readonly=True)
new_date_from = fields.Date(string="New start date", readonly=True)
old_date_to = fields.Date(string="Old end date", readonly=True)
new_date_to = fields.Date(string="New end date", readonly=True)
old_is_enabled = fields.Boolean(string="Active before edit", readonly=True)
new_is_enabled = fields.Boolean(string="Active after edit", readonly=True)
@api.model
def _prepare_create_from_vals(
self, old_role_line_values_by_user, new_role_line_values_by_user
):
role_history_line_vals_by_role_line = {}
for key, value in new_role_line_values_by_user.items():
old_vals = old_role_line_values_by_user.get(key, {})
new_vals = value
# Manage deletion of role lines and old values of modified lines
for role_line_id, role_line_vals in old_vals.items():
action = "unlink" if role_line_id not in new_vals else "edit"
if action == "edit":
# Skip if no change
if not any(
role_line_vals[k] != new_vals[role_line_id][k]
for k in role_line_vals
):
continue
role_history_line_vals_by_role_line.setdefault(role_line_id, {})
role_history_line_vals_by_role_line[role_line_id].update(
{
"performed_action": action,
"user_id": role_line_vals["user_id"],
"old_role_id": role_line_vals["role_id"],
"old_date_from": role_line_vals["date_from"],
"old_date_to": role_line_vals["date_to"],
"old_is_enabled": role_line_vals["is_enabled"],
}
)
# Manage addition of role lines and new values of modified ones
for role_line_id, role_line_vals in new_vals.items():
action = "add" if role_line_id not in old_vals else "edit"
if action == "edit":
# Skip if no change
if not any(
role_line_vals[k] != old_vals[role_line_id][k]
for k in role_line_vals
):
continue
role_history_line_vals_by_role_line.setdefault(role_line_id, {})
role_history_line_vals_by_role_line[role_line_id].update(
{
"performed_action": action,
"user_id": role_line_vals["user_id"],
"new_role_id": role_line_vals["role_id"],
"new_date_from": role_line_vals["date_from"],
"new_date_to": role_line_vals["date_to"],
"new_is_enabled": role_line_vals["is_enabled"],
}
)
return role_history_line_vals_by_role_line
@api.model
def create_from_vals(
self, old_role_line_values_by_user, new_role_line_values_by_user
):
"""
This method creates user role line history objects based on given
old/new values.
old_role_line_values_by_user and new_role_line_values_by_user are like:
{user_id:
{role_line_id:
{role_line_values},
},
}
"""
role_history_line_vals_by_role_line = self._prepare_create_from_vals(
old_role_line_values_by_user, new_role_line_values_by_user
)
# Create the history lines with sudo
# (nobody has the create right)
self.sudo().create(list(role_history_line_vals_by_role_line.values()))
old_date_from = fields.Date(string="Old start date")
new_date_from = fields.Date(string="New start date")
old_date_to = fields.Date(string="Old end date")
new_date_to = fields.Date(string="New end date")
old_is_enabled = fields.Boolean(string="Active before edit")
new_is_enabled = fields.Boolean(string="Active after edit")

View File

@@ -1,65 +1,22 @@
# Copyright 2019 ACSONE SA/NV
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo import _, api, fields, models
from odoo import _, fields, models
class ResUsers(models.Model):
_inherit = "res.users"
last_role_line_modification = fields.Datetime(
string="Last roles modification", readonly=True
compute="_compute_last_role_line_modification",
)
@api.model
def _prepare_role_line_history_dict(self, role_line):
return {
"user_id": role_line.user_id.id,
"role_id": role_line.role_id.id,
"date_from": role_line.date_from,
"date_to": role_line.date_to,
"is_enabled": role_line.is_enabled,
}
def _get_role_line_values_by_user(self):
role_line_values_by_user = {}
for rec in self:
role_line_values_by_user.setdefault(rec, {})
for role_line in rec.role_line_ids:
role_line_values_by_user[rec][
role_line.id
] = self._prepare_role_line_history_dict(role_line)
return role_line_values_by_user
@api.model_create_multi
def create(self, vals_list):
res = super().create(vals_list)
if all("role_line_ids" not in vals for vals in vals_list):
return res
new_role_line_values_by_user = res._get_role_line_values_by_user()
new_role_line_to_create = {}
users = self.browse()
for user, lines in new_role_line_values_by_user.items():
if lines:
new_role_line_to_create[user] = lines
user |= user
self.env["base.user.role.line.history"].create_from_vals(
{}, new_role_line_to_create
)
users.last_role_line_modification = fields.Datetime.now()
return res
def write(self, vals):
if "role_line_ids" not in vals:
return super().write(vals)
old_role_line_values_by_user = self._get_role_line_values_by_user()
vals["last_role_line_modification"] = fields.Datetime.now()
res = super().write(vals)
new_role_line_values_by_user = self._get_role_line_values_by_user()
self.env["base.user.role.line.history"].create_from_vals(
old_role_line_values_by_user, new_role_line_values_by_user
)
return res
def _compute_last_role_line_modification(self):
for user in self:
res = self.env["base.user.role.line.history"].search(
[("user_id", "=", user.id)], limit=1, order="id desc"
)
user.last_role_line_modification = res.create_date if res else False
def show_role_lines_history(self): # pragma: no cover
self.ensure_one()

View File

@@ -0,0 +1,72 @@
from odoo import api, models
class ResUsersRoleLine(models.Model):
_inherit = "res.users.role.line"
def write(self, vals):
history_lines = []
for line in self:
history_line = {
"performed_action": "edit",
"user_id": line.user_id.id,
"old_role_id": line.role_id.id,
"old_date_from": line.date_from,
"old_date_to": line.date_to,
"old_is_enabled": line.is_enabled,
"new_role_id": vals.get("role_id", line.role_id.id),
"new_date_from": vals.get("date_from", line.date_from),
"new_date_to": vals.get("date_to", line.date_to),
"new_is_enabled": vals.get("is_enabled", line.is_enabled),
}
if (
history_line["old_role_id"] == history_line["new_role_id"]
and history_line["old_date_from"] == history_line["new_date_from"]
and history_line["old_date_to"] == history_line["new_date_to"]
and history_line["old_is_enabled"] == history_line["new_is_enabled"]
):
continue
history_lines.append(history_line)
res = super().write(vals)
self.env["base.user.role.line.history"].sudo().create(history_lines)
return res
@api.model_create_multi
def create(self, vals_list):
history_lines = []
for line in vals_list:
history_line = {
"performed_action": "add",
"user_id": line.get("user_id", False),
"new_role_id": line.get("role_id", False),
"new_date_from": line.get("date_from", False),
"new_date_to": line.get("date_to", False),
"new_is_enabled": line.get("is_enabled", True),
}
history_lines.append(history_line)
res = super().create(vals_list)
self.env["base.user.role.line.history"].sudo().create(history_lines)
return res
def unlink(self):
history_lines = []
for line in self:
history_line = {
"performed_action": "unlink",
"user_id": line.user_id.id,
"old_role_id": line.role_id.id,
"old_date_from": line.date_from,
"old_date_to": line.date_to,
"old_is_enabled": line.is_enabled,
}
history_lines.append(history_line)
res = super().unlink()
self.env["base.user.role.line.history"].sudo().create(history_lines)
return res

View File

@@ -1,2 +1,4 @@
- Benoit Aimont \<<benoit.aimont@acsone.eu>\> (<https://acsone.eu>)
- Thomas Binsfeld \<<thomas.binsfeld@acsone.eu>\> (<https://acsone.eu>)
- Christopher Rogos \<<crogos@gmail.com>\> (<https://glueckkanja.com>)
- Mohamed Osman \<<mohamed.osman3@yahoo.com>\> (<https://glueckkanja.com>)

View File

@@ -8,10 +8,11 @@
/*
:Author: David Goodger (goodger@python.org)
:Id: $Id: html4css1.css 8954 2022-01-20 10:10:25Z milde $
:Id: $Id: html4css1.css 9511 2024-01-13 09:50:07Z milde $
:Copyright: This stylesheet has been placed in the public domain.
Default cascading style sheet for the HTML output of Docutils.
Despite the name, some widely supported CSS2 features are used.
See https://docutils.sourceforge.io/docs/howto/html-stylesheets.html for how to
customize this style sheet.
@@ -274,7 +275,7 @@ pre.literal-block, pre.doctest-block, pre.math, pre.code {
margin-left: 2em ;
margin-right: 2em }
pre.code .ln { color: grey; } /* line numbers */
pre.code .ln { color: gray; } /* line numbers */
pre.code, code { background-color: #eeeeee }
pre.code .comment, code .comment { color: #5C6576 }
pre.code .keyword, code .keyword { color: #3B0D06; font-weight: bold }
@@ -300,7 +301,7 @@ span.option {
span.pre {
white-space: pre }
span.problematic {
span.problematic, pre.problematic {
color: red }
span.section-subtitle {
@@ -419,12 +420,16 @@ If you spotted it first, help us to smash it by providing a detailed and welcome
<ul class="simple">
<li>Benoit Aimont &lt;<a class="reference external" href="mailto:benoit.aimont&#64;acsone.eu">benoit.aimont&#64;acsone.eu</a>&gt; (<a class="reference external" href="https://acsone.eu">https://acsone.eu</a>)</li>
<li>Thomas Binsfeld &lt;<a class="reference external" href="mailto:thomas.binsfeld&#64;acsone.eu">thomas.binsfeld&#64;acsone.eu</a>&gt; (<a class="reference external" href="https://acsone.eu">https://acsone.eu</a>)</li>
<li>Christopher Rogos &lt;<a class="reference external" href="mailto:crogos&#64;gmail.com">crogos&#64;gmail.com</a>&gt; (<a class="reference external" href="https://glueckkanja.com">https://glueckkanja.com</a>)</li>
<li>Mohamed Osman &lt;<a class="reference external" href="mailto:mohamed.osman3&#64;yahoo.com">mohamed.osman3&#64;yahoo.com</a>&gt; (<a class="reference external" href="https://glueckkanja.com">https://glueckkanja.com</a>)</li>
</ul>
</div>
<div class="section" id="maintainers">
<h2><a class="toc-backref" href="#toc-entry-7">Maintainers</a></h2>
<p>This module is maintained by the OCA.</p>
<a class="reference external image-reference" href="https://odoo-community.org"><img alt="Odoo Community Association" src="https://odoo-community.org/logo.png" /></a>
<a class="reference external image-reference" href="https://odoo-community.org">
<img alt="Odoo Community Association" src="https://odoo-community.org/logo.png" />
</a>
<p>OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and
promote its widespread use.</p>

View File

@@ -1 +1,2 @@
from . import test_base_user_role_history
from . import test_res_users_role

View File

@@ -6,10 +6,11 @@ from datetime import date, timedelta
from odoo.tests.common import TransactionCase
# DEPRECATED: This tests are deprecated but stay to show that the new code is working.
class TestBaseUserRoleHistory(TransactionCase):
@classmethod
def setUpClass(cls):
super(TestBaseUserRoleHistory, cls).setUpClass()
super().setUpClass()
cls.env = cls.env(context=dict(cls.env.context, tracking_disable=True))
# MODELS
@@ -138,4 +139,4 @@ class TestBaseUserRoleHistory(TransactionCase):
"""
new_user = self.user_model.create({"login": "new_user", "name": "new_user"})
history_lines = self.history_line_model.search([("user_id", "=", new_user.id)])
self.assertFalse(history_lines)
self.assertEqual(len(history_lines), 0)

View File

@@ -0,0 +1,181 @@
from datetime import date
from odoo.tests.common import TransactionCase
class TestResUsersRole(TransactionCase):
@classmethod
def setUpClass(cls):
super().setUpClass()
cls.ResUsers = cls.env["res.users"]
cls.ResUsersRole = cls.env["res.users.role"]
cls.ResUsersRoleLine = cls.env["res.users.role.line"]
cls.BaseUserRoleLineHistory = cls.env["base.user.role.line.history"]
cls.user = cls.ResUsers.create(
{
"name": "Test user 01",
"login": "test1",
}
)
def test_create_adds_history(self):
# act
role_01 = self.ResUsersRole.create(
{"name": "Test role 01", "line_ids": [(0, 0, {"user_id": self.user.id})]}
)
# assert
self.assertTrue(role_01.name, "Test role 01")
history_lines_all = self.BaseUserRoleLineHistory.search(
[("user_id", "=", self.user.id)]
)
self.assertEqual(len(history_lines_all), 1)
history_line_1 = history_lines_all[0]
self.assertEqual(history_line_1.user_id, self.user)
self.assertEqual(history_line_1.performed_action, "add")
self.assertFalse(history_line_1.old_role_id)
self.assertEqual(history_line_1.new_role_id, role_01)
self.assertFalse(history_line_1.old_is_enabled)
self.assertTrue(history_line_1.new_is_enabled)
def test_write_adds_history_entry(self):
# arrange
role_01 = self.ResUsersRole.create(
{"name": "Test role 01", "line_ids": [(0, 0, {"user_id": self.user.id})]}
)
role_01_line = role_01.line_ids[0]
# act
role_01_line.write(
{
"date_from": "2024-05-01",
"date_to": "2024-05-30",
}
)
# assert
history_lines_all = self.BaseUserRoleLineHistory.search(
[("user_id", "=", self.user.id)]
)
self.assertEqual(len(history_lines_all), 2)
history_lines_add = history_lines_all.filtered(
lambda x: x.performed_action == "edit"
)
self.assertEqual(len(history_lines_add), 1)
history_line_1 = history_lines_add[0]
self.assertEqual(history_line_1.performed_action, "edit")
self.assertFalse(history_line_1.old_date_from)
self.assertEqual(history_line_1.new_date_from, date(2024, 5, 1))
self.assertFalse(history_line_1.old_date_to)
self.assertEqual(history_line_1.new_date_to, date(2024, 5, 30))
self.assertTrue(history_line_1.old_is_enabled)
self.assertTrue(history_line_1.new_is_enabled)
def test_write_no_history_entry(self):
# arrange
role_01 = self.ResUsersRole.create(
{
"name": "Test role 01",
"line_ids": [
(
0,
0,
{
"user_id": self.user.id,
"date_from": "2024-05-01",
"date_to": "2024-05-30",
},
)
],
}
)
role_01_line = role_01.line_ids[0]
# act
# write same values again
role_01_line.write(
{
"date_from": "2024-05-01",
"date_to": "2024-05-30",
}
)
# assert
history_lines_all = self.BaseUserRoleLineHistory.search(
[("user_id", "=", self.user.id)]
)
self.assertTrue(len(history_lines_all), 1)
history_lines_edit = history_lines_all.filtered(
lambda x: x.performed_action == "edit"
)
self.assertTrue(len(history_lines_edit), 0)
def test_create_adds_history_entry(self):
# arrange
role_01 = self.ResUsersRole.create({"name": "Test role 01", "line_ids": []})
# act
self.ResUsersRoleLine.create(
{
"role_id": role_01.id,
"user_id": self.user.id,
"date_from": "2024-05-01",
"date_to": "2024-05-30",
}
)
# assert
history_line_add = self.BaseUserRoleLineHistory.search(
[("user_id", "=", self.user.id), ("performed_action", "=", "add")]
)
self.assertEqual(len(history_line_add), 1)
history_line_1 = history_line_add[0]
self.assertFalse(history_line_1.old_role_id)
self.assertEqual(history_line_1.new_role_id, role_01)
self.assertFalse(history_line_1.old_date_from)
self.assertEqual(history_line_1.new_date_from, date(2024, 5, 1))
self.assertFalse(history_line_1.old_date_to)
self.assertEqual(history_line_1.new_date_to, date(2024, 5, 30))
self.assertFalse(history_line_1.old_is_enabled)
self.assertTrue(history_line_1.new_is_enabled)
def test_unlink_adds_history_entry(self):
# arrange
role_01 = self.ResUsersRole.create(
{"name": "Test role 01", "line_ids": [(0, 0, {"user_id": self.user.id})]}
)
# act
role_01.line_ids[0].unlink()
# assert
history_line_all = self.BaseUserRoleLineHistory.search(
[("user_id", "=", self.user.id)]
)
self.assertEqual(len(history_line_all), 2)
history_lines_unlink = history_line_all.filtered(
lambda x: x.performed_action == "unlink"
)
self.assertEqual(len(history_lines_unlink), 1)
history_line_1 = history_lines_unlink[0]
self.assertEqual(history_line_1.old_role_id, role_01)
self.assertFalse(history_line_1.new_role_id)
self.assertFalse(history_line_1.old_date_from)
self.assertFalse(history_line_1.new_date_from)
self.assertFalse(history_line_1.old_date_to)
self.assertFalse(history_line_1.new_date_to)
self.assertTrue(history_line_1.old_is_enabled)
self.assertFalse(history_line_1.new_is_enabled)

View File

@@ -15,17 +15,17 @@
<sheet>
<group name="group_main" col="4">
<field name="create_uid" string="Editor" />
<field name="performed_action" />
<field name="user_id" string="Edited user" />
<field name="performed_action" readonly="True" />
<field name="user_id" string="Edited user" readonly="True" />
<field name="create_date" string="Date and time of edition" />
<field name="old_role_id" />
<field name="new_role_id" />
<field name="old_date_from" />
<field name="new_date_from" />
<field name="old_date_to" />
<field name="new_date_to" />
<field name="old_is_enabled" />
<field name="new_is_enabled" />
<field name="old_role_id" readonly="True" />
<field name="new_role_id" readonly="True" />
<field name="old_date_from" readonly="True" />
<field name="new_date_from" readonly="True" />
<field name="old_date_to" readonly="True" />
<field name="new_date_to" readonly="True" />
<field name="old_is_enabled" readonly="True" />
<field name="new_is_enabled" readonly="True" />
</group>
</sheet>
</form>