server-tools/database_cleanup/models/purge_properties.py

155 lines
5.6 KiB
Python

# Copyright 2017 Therp BV <http://therp.nl>
# Copyright 2021 Camptocamp <https://camptocamp.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
# pylint: disable=consider-merging-classes-inherited
from odoo import api, fields, models
REASON_DUPLICATE = "REASON_DUPLICATE"
REASON_DEFAULT = "REASON_DEFAULT"
REASON_DEFAULT_FALSE = "REASON_DEFAULT_FALSE"
REASON_UNKNOWN_MODEL = "REASON_UNKNOWN_MODEL"
class CleanupPurgeLineProperty(models.TransientModel):
_inherit = "cleanup.purge.line"
_name = "cleanup.purge.line.property"
_description = "Cleanup Purge Line Property"
wizard_id = fields.Many2one(
"cleanup.purge.wizard.property", "Purge Wizard", readonly=True
)
property_id = fields.Many2one("ir.property")
reason = fields.Selection(
[
(REASON_DUPLICATE, "Duplicated property"),
(REASON_DEFAULT, "Same value as default"),
(REASON_DEFAULT_FALSE, "Empty default property"),
(REASON_UNKNOWN_MODEL, "Unknown model"),
]
)
def purge(self):
"""Delete properties"""
self.write({"purged": True})
return self.mapped("property_id").unlink()
class CleanupPurgeWizardProperty(models.TransientModel):
_inherit = "cleanup.purge.wizard"
_name = "cleanup.purge.wizard.property"
_description = "Purge properties"
@api.model
def find(self):
"""
Search property records which are duplicated or the same as the default
"""
result = []
default_properties = self.env["ir.property"].search(
[
("res_id", "=", False),
]
)
handled_field_ids = []
for prop in default_properties:
value = None
try:
value = prop.get_by_record()
except KeyError:
result.append(
{
"name": "{}@{}: {}".format(prop.name, prop.res_id, value),
"property_id": prop.id,
"reason": REASON_UNKNOWN_MODEL,
}
)
continue
if not value:
result.append(
{
"name": "{}@{}: {}".format(prop.name, prop.res_id, value),
"property_id": prop.id,
"reason": REASON_DEFAULT_FALSE,
}
)
continue
if prop.fields_id.id in handled_field_ids:
continue
domain = [
("id", "!=", prop.id),
("fields_id", "=", prop.fields_id.id),
# =? explicitly tests for None or False, not falsyness
("value_float", "=?", prop.value_float or False),
("value_integer", "=?", prop.value_integer or False),
("value_text", "=?", prop.value_text or False),
("value_binary", "=?", prop.value_binary or False),
("value_reference", "=?", prop.value_reference or False),
("value_datetime", "=?", prop.value_datetime or False),
]
if prop.company_id:
domain.append(("company_id", "=", prop.company_id.id))
else:
domain.extend(
[
"|",
("company_id", "=", False),
(
"company_id",
"in",
self.env["res.company"]
.search(
[
(
"id",
"not in",
default_properties.filtered(
lambda x: x.company_id
and x.fields_id == prop.fields_id
).ids,
)
]
)
.ids,
),
]
)
for redundant_property in self.env["ir.property"].search(domain):
result.append(
{
"name": "{}@{}: {}".format(
prop.name, redundant_property.res_id, prop.get_by_record()
),
"property_id": redundant_property.id,
"reason": REASON_DEFAULT,
}
)
handled_field_ids.append(prop.fields_id.id)
self.env.cr.execute(
"""
with grouped_properties(ids, cnt) as (
select array_agg(id), count(*)
from ir_property group by res_id, company_id, fields_id
)
select ids from grouped_properties where cnt > 1
"""
)
for (ids,) in self.env.cr.fetchall():
# odoo uses the first property found by search
for prop in self.env["ir.property"].search([("id", "in", ids)])[1:]:
result.append(
{
"name": "{}@{}: {}".format(
prop.name, prop.res_id, prop.get_by_record()
),
"property_id": prop.id,
"reason": REASON_DUPLICATE,
}
)
return result
purge_line_ids = fields.One2many(
"cleanup.purge.line.property", "wizard_id", "Properties to purge"
)