[IMP] database_cleanup: black, isort, prettier
parent
5d0be1e42e
commit
d1acfa4b4a
|
@ -1,23 +1,23 @@
|
||||||
# Copyright 2014-2016 Therp BV <http://therp.nl>
|
# Copyright 2014-2016 Therp BV <http://therp.nl>
|
||||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||||
{
|
{
|
||||||
'name': 'Database cleanup',
|
"name": "Database cleanup",
|
||||||
'version': '12.0.1.0.1',
|
"version": "12.0.1.0.1",
|
||||||
'author': "Therp BV,Odoo Community Association (OCA)",
|
"author": "Therp BV,Odoo Community Association (OCA)",
|
||||||
'depends': ['base'],
|
"depends": ["base"],
|
||||||
'license': 'AGPL-3',
|
"license": "AGPL-3",
|
||||||
'category': 'Tools',
|
"category": "Tools",
|
||||||
'data': [
|
"data": [
|
||||||
"views/purge_wizard.xml",
|
"views/purge_wizard.xml",
|
||||||
'views/purge_menus.xml',
|
"views/purge_menus.xml",
|
||||||
'views/purge_modules.xml',
|
"views/purge_modules.xml",
|
||||||
'views/purge_models.xml',
|
"views/purge_models.xml",
|
||||||
'views/purge_columns.xml',
|
"views/purge_columns.xml",
|
||||||
'views/purge_tables.xml',
|
"views/purge_tables.xml",
|
||||||
'views/purge_data.xml',
|
"views/purge_data.xml",
|
||||||
"views/create_indexes.xml",
|
"views/create_indexes.xml",
|
||||||
'views/purge_properties.xml',
|
"views/purge_properties.xml",
|
||||||
'views/menu.xml',
|
"views/menu.xml",
|
||||||
],
|
],
|
||||||
'installable': True,
|
"installable": True,
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,11 +14,9 @@ class IdentifierAdapter(ISQLQuote):
|
||||||
|
|
||||||
def getquoted(self):
|
def getquoted(self):
|
||||||
def is_identifier_char(c):
|
def is_identifier_char(c):
|
||||||
return c.isalnum() or c in ['_', '$']
|
return c.isalnum() or c in ["_", "$"]
|
||||||
|
|
||||||
format_string = '"%s"'
|
format_string = '"%s"'
|
||||||
if not self.quote:
|
if not self.quote:
|
||||||
format_string = '%s'
|
format_string = "%s"
|
||||||
return format_string % ''.join(
|
return format_string % "".join(filter(is_identifier_char, self.identifier))
|
||||||
filter(is_identifier_char, self.identifier)
|
|
||||||
)
|
|
||||||
|
|
|
@ -1,26 +1,27 @@
|
||||||
# Copyright 2017 Therp BV <http://therp.nl>
|
# Copyright 2017 Therp BV <http://therp.nl>
|
||||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||||
# pylint: disable=consider-merging-classes-inherited
|
# pylint: disable=consider-merging-classes-inherited
|
||||||
from ..identifier_adapter import IdentifierAdapter
|
|
||||||
from odoo import api, fields, models
|
from odoo import api, fields, models
|
||||||
|
|
||||||
|
from ..identifier_adapter import IdentifierAdapter
|
||||||
|
|
||||||
|
|
||||||
class CreateIndexesLine(models.TransientModel):
|
class CreateIndexesLine(models.TransientModel):
|
||||||
_inherit = 'cleanup.purge.line'
|
_inherit = "cleanup.purge.line"
|
||||||
_name = 'cleanup.create_indexes.line'
|
_name = "cleanup.create_indexes.line"
|
||||||
|
|
||||||
purged = fields.Boolean('Created')
|
purged = fields.Boolean("Created")
|
||||||
wizard_id = fields.Many2one('cleanup.create_indexes.wizard')
|
wizard_id = fields.Many2one("cleanup.create_indexes.wizard")
|
||||||
field_id = fields.Many2one('ir.model.fields', required=True)
|
field_id = fields.Many2one("ir.model.fields", required=True)
|
||||||
|
|
||||||
@api.multi
|
@api.multi
|
||||||
def purge(self):
|
def purge(self):
|
||||||
tables = set()
|
tables = set()
|
||||||
for field in self.mapped('field_id'):
|
for field in self.mapped("field_id"):
|
||||||
model = self.env[field.model]
|
model = self.env[field.model]
|
||||||
name = '%s_%s_index' % (model._table, field.name)
|
name = "{}_{}_index".format(model._table, field.name)
|
||||||
self.env.cr.execute(
|
self.env.cr.execute(
|
||||||
'create index %s ON %s (%s)',
|
"create index %s ON %s (%s)",
|
||||||
(
|
(
|
||||||
IdentifierAdapter(name, quote=False),
|
IdentifierAdapter(name, quote=False),
|
||||||
IdentifierAdapter(model._table),
|
IdentifierAdapter(model._table),
|
||||||
|
@ -29,54 +30,66 @@ class CreateIndexesLine(models.TransientModel):
|
||||||
)
|
)
|
||||||
tables.add(model._table)
|
tables.add(model._table)
|
||||||
for table in tables:
|
for table in tables:
|
||||||
self.env.cr.execute(
|
self.env.cr.execute("analyze %s", (IdentifierAdapter(model._table),))
|
||||||
'analyze %s', (IdentifierAdapter(model._table),)
|
self.write(
|
||||||
)
|
{
|
||||||
self.write({
|
"purged": True,
|
||||||
'purged': True,
|
}
|
||||||
})
|
)
|
||||||
|
|
||||||
|
|
||||||
class CreateIndexesWizard(models.TransientModel):
|
class CreateIndexesWizard(models.TransientModel):
|
||||||
_inherit = 'cleanup.purge.wizard'
|
_inherit = "cleanup.purge.wizard"
|
||||||
_name = 'cleanup.create_indexes.wizard'
|
_name = "cleanup.create_indexes.wizard"
|
||||||
_description = 'Create indexes'
|
_description = "Create indexes"
|
||||||
|
|
||||||
purge_line_ids = fields.One2many(
|
purge_line_ids = fields.One2many(
|
||||||
'cleanup.create_indexes.line', 'wizard_id',
|
"cleanup.create_indexes.line",
|
||||||
|
"wizard_id",
|
||||||
)
|
)
|
||||||
|
|
||||||
@api.multi
|
@api.multi
|
||||||
def find(self):
|
def find(self):
|
||||||
res = list()
|
res = list()
|
||||||
for field in self.env['ir.model.fields'].search([
|
for field in self.env["ir.model.fields"].search(
|
||||||
('index', '=', True),
|
[
|
||||||
]):
|
("index", "=", True),
|
||||||
|
]
|
||||||
|
):
|
||||||
if field.model not in self.env.registry:
|
if field.model not in self.env.registry:
|
||||||
continue
|
continue
|
||||||
model = self.env[field.model]
|
model = self.env[field.model]
|
||||||
name = '%s_%s_index' % (model._table, field.name)
|
name = "{}_{}_index".format(model._table, field.name)
|
||||||
self.env.cr.execute(
|
self.env.cr.execute(
|
||||||
'select indexname from pg_indexes '
|
"select indexname from pg_indexes "
|
||||||
'where indexname=%s and tablename=%s',
|
"where indexname=%s and tablename=%s",
|
||||||
(name, model._table)
|
(name, model._table),
|
||||||
)
|
)
|
||||||
if self.env.cr.rowcount:
|
if self.env.cr.rowcount:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
self.env.cr.execute(
|
self.env.cr.execute(
|
||||||
'select a.attname '
|
"select a.attname "
|
||||||
'from pg_attribute a '
|
"from pg_attribute a "
|
||||||
'join pg_class c on a.attrelid=c.oid '
|
"join pg_class c on a.attrelid=c.oid "
|
||||||
'join pg_tables t on t.tablename=c.relname '
|
"join pg_tables t on t.tablename=c.relname "
|
||||||
'where attname=%s and c.relname=%s',
|
"where attname=%s and c.relname=%s",
|
||||||
(field.name, model._table,)
|
(
|
||||||
|
field.name,
|
||||||
|
model._table,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
if not self.env.cr.rowcount:
|
if not self.env.cr.rowcount:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
res.append((0, 0, {
|
res.append(
|
||||||
'name': '%s.%s' % (field.model, field.name),
|
(
|
||||||
'field_id': field.id,
|
0,
|
||||||
}))
|
0,
|
||||||
|
{
|
||||||
|
"name": "{}.{}".format(field.model, field.name),
|
||||||
|
"field_id": field.id,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
)
|
||||||
return res
|
return res
|
||||||
|
|
|
@ -3,18 +3,19 @@
|
||||||
# pylint: disable=consider-merging-classes-inherited
|
# pylint: disable=consider-merging-classes-inherited
|
||||||
from odoo import _, api, fields, models
|
from odoo import _, api, fields, models
|
||||||
from odoo.exceptions import UserError
|
from odoo.exceptions import UserError
|
||||||
|
|
||||||
from ..identifier_adapter import IdentifierAdapter
|
from ..identifier_adapter import IdentifierAdapter
|
||||||
|
|
||||||
|
|
||||||
class CleanupPurgeLineColumn(models.TransientModel):
|
class CleanupPurgeLineColumn(models.TransientModel):
|
||||||
_inherit = 'cleanup.purge.line'
|
_inherit = "cleanup.purge.line"
|
||||||
_name = 'cleanup.purge.line.column'
|
_name = "cleanup.purge.line.column"
|
||||||
_description = 'Purge Column Wizard Lines'
|
_description = "Purge Column Wizard Lines"
|
||||||
|
|
||||||
model_id = fields.Many2one('ir.model', 'Model', required=True,
|
model_id = fields.Many2one("ir.model", "Model", required=True, ondelete="CASCADE")
|
||||||
ondelete='CASCADE')
|
|
||||||
wizard_id = fields.Many2one(
|
wizard_id = fields.Many2one(
|
||||||
'cleanup.purge.wizard.column', 'Purge Wizard', readonly=True)
|
"cleanup.purge.wizard.column", "Purge Wizard", readonly=True
|
||||||
|
)
|
||||||
|
|
||||||
@api.multi
|
@api.multi
|
||||||
def purge(self):
|
def purge(self):
|
||||||
|
@ -24,8 +25,9 @@ class CleanupPurgeLineColumn(models.TransientModel):
|
||||||
if self:
|
if self:
|
||||||
objs = self
|
objs = self
|
||||||
else:
|
else:
|
||||||
objs = self.env['cleanup.purge.line.column']\
|
objs = self.env["cleanup.purge.line.column"].browse(
|
||||||
.browse(self._context.get('active_ids'))
|
self._context.get("active_ids")
|
||||||
|
)
|
||||||
for line in objs:
|
for line in objs:
|
||||||
if line.purged:
|
if line.purged:
|
||||||
continue
|
continue
|
||||||
|
@ -34,24 +36,23 @@ class CleanupPurgeLineColumn(models.TransientModel):
|
||||||
# Inheritance such as stock.picking.in from stock.picking
|
# Inheritance such as stock.picking.in from stock.picking
|
||||||
# can lead to double attempts at removal
|
# can lead to double attempts at removal
|
||||||
self.env.cr.execute(
|
self.env.cr.execute(
|
||||||
'SELECT count(attname) FROM pg_attribute '
|
"SELECT count(attname) FROM pg_attribute "
|
||||||
'WHERE attrelid = '
|
"WHERE attrelid = "
|
||||||
'( SELECT oid FROM pg_class WHERE relname = %s ) '
|
"( SELECT oid FROM pg_class WHERE relname = %s ) "
|
||||||
'AND attname = %s',
|
"AND attname = %s",
|
||||||
(model_pool._table, line.name))
|
(model_pool._table, line.name),
|
||||||
|
)
|
||||||
if not self.env.cr.fetchone()[0]:
|
if not self.env.cr.fetchone()[0]:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
self.logger.info(
|
self.logger.info(
|
||||||
'Dropping column %s from table %s',
|
"Dropping column %s from table %s", line.name, model_pool._table
|
||||||
line.name, model_pool._table)
|
)
|
||||||
self.env.cr.execute(
|
self.env.cr.execute(
|
||||||
'ALTER TABLE %s DROP COLUMN %s',
|
"ALTER TABLE %s DROP COLUMN %s",
|
||||||
(
|
(IdentifierAdapter(model_pool._table), IdentifierAdapter(line.name)),
|
||||||
IdentifierAdapter(model_pool._table),
|
)
|
||||||
IdentifierAdapter(line.name)
|
line.write({"purged": True})
|
||||||
))
|
|
||||||
line.write({'purged': True})
|
|
||||||
# we need this commit because the ORM will deadlock if
|
# we need this commit because the ORM will deadlock if
|
||||||
# we still have a pending transaction
|
# we still have a pending transaction
|
||||||
self.env.cr.commit() # pylint: disable=invalid-commit
|
self.env.cr.commit() # pylint: disable=invalid-commit
|
||||||
|
@ -59,15 +60,15 @@ class CleanupPurgeLineColumn(models.TransientModel):
|
||||||
|
|
||||||
|
|
||||||
class CleanupPurgeWizardColumn(models.TransientModel):
|
class CleanupPurgeWizardColumn(models.TransientModel):
|
||||||
_inherit = 'cleanup.purge.wizard'
|
_inherit = "cleanup.purge.wizard"
|
||||||
_name = 'cleanup.purge.wizard.column'
|
_name = "cleanup.purge.wizard.column"
|
||||||
_description = 'Purge columns'
|
_description = "Purge columns"
|
||||||
|
|
||||||
# List of known columns in use without corresponding fields
|
# List of known columns in use without corresponding fields
|
||||||
# Format: {table: [fields]}
|
# Format: {table: [fields]}
|
||||||
blacklist = {
|
blacklist = {
|
||||||
'wkf_instance': ['uid'], # lp:1277899
|
"wkf_instance": ["uid"], # lp:1277899
|
||||||
'res_users': ['password', 'password_crypt'],
|
"res_users": ["password", "password_crypt"],
|
||||||
}
|
}
|
||||||
|
|
||||||
@api.model
|
@api.model
|
||||||
|
@ -77,12 +78,14 @@ class CleanupPurgeWizardColumn(models.TransientModel):
|
||||||
Iterate on the database columns to identify columns
|
Iterate on the database columns to identify columns
|
||||||
of fields which have been removed
|
of fields which have been removed
|
||||||
"""
|
"""
|
||||||
columns = list(set([
|
columns = list(
|
||||||
column.name
|
{
|
||||||
for model_pool in model_pools
|
column.name
|
||||||
for column in model_pool._fields.values()
|
for model_pool in model_pools
|
||||||
if not (column.compute is not None and not column.store)
|
for column in model_pool._fields.values()
|
||||||
]))
|
if not (column.compute is not None and not column.store)
|
||||||
|
}
|
||||||
|
)
|
||||||
columns += models.MAGIC_COLUMNS
|
columns += models.MAGIC_COLUMNS
|
||||||
columns += self.blacklist.get(model_pools[0]._table, [])
|
columns += self.blacklist.get(model_pools[0]._table, [])
|
||||||
|
|
||||||
|
@ -92,7 +95,8 @@ class CleanupPurgeWizardColumn(models.TransientModel):
|
||||||
"AND pg_catalog.format_type(a.atttypid, a.atttypmod) "
|
"AND pg_catalog.format_type(a.atttypid, a.atttypmod) "
|
||||||
"NOT IN ('cid', 'tid', 'oid', 'xid') "
|
"NOT IN ('cid', 'tid', 'oid', 'xid') "
|
||||||
"AND a.attname NOT IN %s",
|
"AND a.attname NOT IN %s",
|
||||||
(model_pools[0]._table, tuple(columns)))
|
(model_pools[0]._table, tuple(columns)),
|
||||||
|
)
|
||||||
return [column for column, in self.env.cr.fetchall()]
|
return [column for column, in self.env.cr.fetchall()]
|
||||||
|
|
||||||
@api.model
|
@api.model
|
||||||
|
@ -109,24 +113,23 @@ class CleanupPurgeWizardColumn(models.TransientModel):
|
||||||
# mapping of tables to tuples (model id, [pool1, pool2, ...])
|
# mapping of tables to tuples (model id, [pool1, pool2, ...])
|
||||||
table2model = {}
|
table2model = {}
|
||||||
|
|
||||||
for model in self.env['ir.model'].search([]):
|
for model in self.env["ir.model"].search([]):
|
||||||
if model.model not in self.env:
|
if model.model not in self.env:
|
||||||
continue
|
continue
|
||||||
model_pool = self.env[model.model]
|
model_pool = self.env[model.model]
|
||||||
if not model_pool._auto:
|
if not model_pool._auto:
|
||||||
continue
|
continue
|
||||||
table2model.setdefault(
|
table2model.setdefault(model_pool._table, (model.id, []))[1].append(
|
||||||
model_pool._table, (model.id, [])
|
model_pool
|
||||||
)[1].append(model_pool)
|
)
|
||||||
|
|
||||||
for table, model_spec in table2model.items():
|
for table, model_spec in table2model.items():
|
||||||
for column in self.get_orphaned_columns(model_spec[1]):
|
for column in self.get_orphaned_columns(model_spec[1]):
|
||||||
res.append((0, 0, {
|
res.append((0, 0, {"name": column, "model_id": model_spec[0]}))
|
||||||
'name': column,
|
|
||||||
'model_id': model_spec[0]}))
|
|
||||||
if not res:
|
if not res:
|
||||||
raise UserError(_('No orphaned columns found'))
|
raise UserError(_("No orphaned columns found"))
|
||||||
return res
|
return res
|
||||||
|
|
||||||
purge_line_ids = fields.One2many(
|
purge_line_ids = fields.One2many(
|
||||||
'cleanup.purge.line.column', 'wizard_id', 'Columns to purge')
|
"cleanup.purge.line.column", "wizard_id", "Columns to purge"
|
||||||
|
)
|
||||||
|
|
|
@ -2,16 +2,18 @@
|
||||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||||
from odoo import _, api, fields, models
|
from odoo import _, api, fields, models
|
||||||
from odoo.exceptions import UserError
|
from odoo.exceptions import UserError
|
||||||
|
|
||||||
from ..identifier_adapter import IdentifierAdapter
|
from ..identifier_adapter import IdentifierAdapter
|
||||||
|
|
||||||
|
|
||||||
class CleanupPurgeLineData(models.TransientModel):
|
class CleanupPurgeLineData(models.TransientModel):
|
||||||
_inherit = 'cleanup.purge.line'
|
_inherit = "cleanup.purge.line"
|
||||||
_name = 'cleanup.purge.line.data'
|
_name = "cleanup.purge.line.data"
|
||||||
|
|
||||||
data_id = fields.Many2one('ir.model.data', 'Data entry')
|
data_id = fields.Many2one("ir.model.data", "Data entry")
|
||||||
wizard_id = fields.Many2one(
|
wizard_id = fields.Many2one(
|
||||||
'cleanup.purge.wizard.data', 'Purge Wizard', readonly=True)
|
"cleanup.purge.wizard.data", "Purge Wizard", readonly=True
|
||||||
|
)
|
||||||
|
|
||||||
@api.multi
|
@api.multi
|
||||||
def purge(self):
|
def purge(self):
|
||||||
|
@ -19,18 +21,19 @@ class CleanupPurgeLineData(models.TransientModel):
|
||||||
if self:
|
if self:
|
||||||
objs = self
|
objs = self
|
||||||
else:
|
else:
|
||||||
objs = self.env['cleanup.purge.line.data']\
|
objs = self.env["cleanup.purge.line.data"].browse(
|
||||||
.browse(self._context.get('active_ids'))
|
self._context.get("active_ids")
|
||||||
|
)
|
||||||
to_unlink = objs.filtered(lambda x: not x.purged and x.data_id)
|
to_unlink = objs.filtered(lambda x: not x.purged and x.data_id)
|
||||||
self.logger.info('Purging data entries: %s', to_unlink.mapped('name'))
|
self.logger.info("Purging data entries: %s", to_unlink.mapped("name"))
|
||||||
to_unlink.mapped('data_id').unlink()
|
to_unlink.mapped("data_id").unlink()
|
||||||
return to_unlink.write({'purged': True})
|
return to_unlink.write({"purged": True})
|
||||||
|
|
||||||
|
|
||||||
class CleanupPurgeWizardData(models.TransientModel):
|
class CleanupPurgeWizardData(models.TransientModel):
|
||||||
_inherit = 'cleanup.purge.wizard'
|
_inherit = "cleanup.purge.wizard"
|
||||||
_name = 'cleanup.purge.wizard.data'
|
_name = "cleanup.purge.wizard.data"
|
||||||
_description = 'Purge data'
|
_description = "Purge data"
|
||||||
|
|
||||||
@api.model
|
@api.model
|
||||||
def find(self):
|
def find(self):
|
||||||
|
@ -41,7 +44,7 @@ class CleanupPurgeWizardData(models.TransientModel):
|
||||||
data_ids = []
|
data_ids = []
|
||||||
unknown_models = []
|
unknown_models = []
|
||||||
self.env.cr.execute("""SELECT DISTINCT(model) FROM ir_model_data""")
|
self.env.cr.execute("""SELECT DISTINCT(model) FROM ir_model_data""")
|
||||||
for model, in self.env.cr.fetchall():
|
for (model,) in self.env.cr.fetchall():
|
||||||
if not model:
|
if not model:
|
||||||
continue
|
continue
|
||||||
if model not in self.env:
|
if model not in self.env:
|
||||||
|
@ -54,19 +57,35 @@ class CleanupPurgeWizardData(models.TransientModel):
|
||||||
AND res_id IS NOT NULL
|
AND res_id IS NOT NULL
|
||||||
AND NOT EXISTS (
|
AND NOT EXISTS (
|
||||||
SELECT id FROM %s WHERE id=ir_model_data.res_id)
|
SELECT id FROM %s WHERE id=ir_model_data.res_id)
|
||||||
""", (model, IdentifierAdapter(self.env[model]._table)))
|
""",
|
||||||
|
(model, IdentifierAdapter(self.env[model]._table)),
|
||||||
|
)
|
||||||
data_ids.extend(data_row for data_row, in self.env.cr.fetchall())
|
data_ids.extend(data_row for data_row, in self.env.cr.fetchall())
|
||||||
data_ids += self.env['ir.model.data'].search([
|
data_ids += (
|
||||||
('model', 'in', unknown_models),
|
self.env["ir.model.data"]
|
||||||
]).ids
|
.search(
|
||||||
for data in self.env['ir.model.data'].browse(data_ids):
|
[
|
||||||
res.append((0, 0, {
|
("model", "in", unknown_models),
|
||||||
'data_id': data.id,
|
]
|
||||||
'name': "%s.%s, object of type %s" % (
|
)
|
||||||
data.module, data.name, data.model)}))
|
.ids
|
||||||
|
)
|
||||||
|
for data in self.env["ir.model.data"].browse(data_ids):
|
||||||
|
res.append(
|
||||||
|
(
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
{
|
||||||
|
"data_id": data.id,
|
||||||
|
"name": "%s.%s, object of type %s"
|
||||||
|
% (data.module, data.name, data.model),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
)
|
||||||
if not res:
|
if not res:
|
||||||
raise UserError(_('No orphaned data entries found'))
|
raise UserError(_("No orphaned data entries found"))
|
||||||
return res
|
return res
|
||||||
|
|
||||||
purge_line_ids = fields.One2many(
|
purge_line_ids = fields.One2many(
|
||||||
'cleanup.purge.line.data', 'wizard_id', 'Data to purge')
|
"cleanup.purge.line.data", "wizard_id", "Data to purge"
|
||||||
|
)
|
||||||
|
|
|
@ -6,12 +6,13 @@ from odoo.exceptions import UserError
|
||||||
|
|
||||||
|
|
||||||
class CleanupPurgeLineMenu(models.TransientModel):
|
class CleanupPurgeLineMenu(models.TransientModel):
|
||||||
_inherit = 'cleanup.purge.line'
|
_inherit = "cleanup.purge.line"
|
||||||
_name = 'cleanup.purge.line.menu'
|
_name = "cleanup.purge.line.menu"
|
||||||
|
|
||||||
wizard_id = fields.Many2one(
|
wizard_id = fields.Many2one(
|
||||||
'cleanup.purge.wizard.menu', 'Purge Wizard', readonly=True)
|
"cleanup.purge.wizard.menu", "Purge Wizard", readonly=True
|
||||||
menu_id = fields.Many2one('ir.ui.menu', 'Menu entry')
|
)
|
||||||
|
menu_id = fields.Many2one("ir.ui.menu", "Menu entry")
|
||||||
|
|
||||||
@api.multi
|
@api.multi
|
||||||
def purge(self):
|
def purge(self):
|
||||||
|
@ -19,18 +20,19 @@ class CleanupPurgeLineMenu(models.TransientModel):
|
||||||
if self:
|
if self:
|
||||||
objs = self
|
objs = self
|
||||||
else:
|
else:
|
||||||
objs = self.env['cleanup.purge.line.menu']\
|
objs = self.env["cleanup.purge.line.menu"].browse(
|
||||||
.browse(self._context.get('active_ids'))
|
self._context.get("active_ids")
|
||||||
|
)
|
||||||
to_unlink = objs.filtered(lambda x: not x.purged and x.menu_id)
|
to_unlink = objs.filtered(lambda x: not x.purged and x.menu_id)
|
||||||
self.logger.info('Purging menu entries: %s', to_unlink.mapped('name'))
|
self.logger.info("Purging menu entries: %s", to_unlink.mapped("name"))
|
||||||
to_unlink.mapped('menu_id').unlink()
|
to_unlink.mapped("menu_id").unlink()
|
||||||
return to_unlink.write({'purged': True})
|
return to_unlink.write({"purged": True})
|
||||||
|
|
||||||
|
|
||||||
class CleanupPurgeWizardMenu(models.TransientModel):
|
class CleanupPurgeWizardMenu(models.TransientModel):
|
||||||
_inherit = 'cleanup.purge.wizard'
|
_inherit = "cleanup.purge.wizard"
|
||||||
_name = 'cleanup.purge.wizard.menu'
|
_name = "cleanup.purge.wizard.menu"
|
||||||
_description = 'Purge menus'
|
_description = "Purge menus"
|
||||||
|
|
||||||
@api.model
|
@api.model
|
||||||
def find(self):
|
def find(self):
|
||||||
|
@ -38,21 +40,30 @@ class CleanupPurgeWizardMenu(models.TransientModel):
|
||||||
Search for models that cannot be instantiated.
|
Search for models that cannot be instantiated.
|
||||||
"""
|
"""
|
||||||
res = []
|
res = []
|
||||||
for menu in self.env['ir.ui.menu'].with_context(active_test=False)\
|
for menu in (
|
||||||
.search([('action', '!=', False)]):
|
self.env["ir.ui.menu"]
|
||||||
if menu.action.type != 'ir.actions.act_window':
|
.with_context(active_test=False)
|
||||||
|
.search([("action", "!=", False)])
|
||||||
|
):
|
||||||
|
if menu.action.type != "ir.actions.act_window":
|
||||||
continue
|
continue
|
||||||
if (menu.action.res_model and menu.action.res_model not in
|
if (menu.action.res_model and menu.action.res_model not in self.env) or (
|
||||||
self.env) or \
|
menu.action.src_model and menu.action.src_model not in self.env
|
||||||
(menu.action.src_model and menu.action.src_model not in
|
):
|
||||||
self.env):
|
res.append(
|
||||||
res.append((0, 0, {
|
(
|
||||||
'name': menu.complete_name,
|
0,
|
||||||
'menu_id': menu.id,
|
0,
|
||||||
}))
|
{
|
||||||
|
"name": menu.complete_name,
|
||||||
|
"menu_id": menu.id,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
)
|
||||||
if not res:
|
if not res:
|
||||||
raise UserError(_('No dangling menu entries found'))
|
raise UserError(_("No dangling menu entries found"))
|
||||||
return res
|
return res
|
||||||
|
|
||||||
purge_line_ids = fields.One2many(
|
purge_line_ids = fields.One2many(
|
||||||
'cleanup.purge.line.menu', 'wizard_id', 'Menus to purge')
|
"cleanup.purge.line.menu", "wizard_id", "Menus to purge"
|
||||||
|
)
|
||||||
|
|
|
@ -1,13 +1,14 @@
|
||||||
# Copyright 2014-2016 Therp BV <http://therp.nl>
|
# Copyright 2014-2016 Therp BV <http://therp.nl>
|
||||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||||
# pylint: disable=consider-merging-classes-inherited
|
# pylint: disable=consider-merging-classes-inherited
|
||||||
from odoo import _, api, models, fields
|
from odoo import _, api, fields, models
|
||||||
from odoo.exceptions import UserError
|
from odoo.exceptions import UserError
|
||||||
|
|
||||||
from odoo.addons.base.models.ir_model import MODULE_UNINSTALL_FLAG
|
from odoo.addons.base.models.ir_model import MODULE_UNINSTALL_FLAG
|
||||||
|
|
||||||
|
|
||||||
class IrModel(models.Model):
|
class IrModel(models.Model):
|
||||||
_inherit = 'ir.model'
|
_inherit = "ir.model"
|
||||||
|
|
||||||
def _drop_table(self):
|
def _drop_table(self):
|
||||||
"""this function crashes for undefined models"""
|
"""this function crashes for undefined models"""
|
||||||
|
@ -22,7 +23,7 @@ class IrModel(models.Model):
|
||||||
|
|
||||||
|
|
||||||
class IrModelFields(models.Model):
|
class IrModelFields(models.Model):
|
||||||
_inherit = 'ir.model.fields'
|
_inherit = "ir.model.fields"
|
||||||
|
|
||||||
@api.multi
|
@api.multi
|
||||||
def _prepare_update(self):
|
def _prepare_update(self):
|
||||||
|
@ -32,12 +33,13 @@ class IrModelFields(models.Model):
|
||||||
|
|
||||||
|
|
||||||
class CleanupPurgeLineModel(models.TransientModel):
|
class CleanupPurgeLineModel(models.TransientModel):
|
||||||
_inherit = 'cleanup.purge.line'
|
_inherit = "cleanup.purge.line"
|
||||||
_name = 'cleanup.purge.line.model'
|
_name = "cleanup.purge.line.model"
|
||||||
_description = 'Purge models'
|
_description = "Purge models"
|
||||||
|
|
||||||
wizard_id = fields.Many2one(
|
wizard_id = fields.Many2one(
|
||||||
'cleanup.purge.wizard.model', 'Purge Wizard', readonly=True)
|
"cleanup.purge.wizard.model", "Purge Wizard", readonly=True
|
||||||
|
)
|
||||||
|
|
||||||
@api.multi
|
@api.multi
|
||||||
def purge(self):
|
def purge(self):
|
||||||
|
@ -46,36 +48,45 @@ class CleanupPurgeLineModel(models.TransientModel):
|
||||||
"""
|
"""
|
||||||
context_flags = {
|
context_flags = {
|
||||||
MODULE_UNINSTALL_FLAG: True,
|
MODULE_UNINSTALL_FLAG: True,
|
||||||
'purge': True,
|
"purge": True,
|
||||||
}
|
}
|
||||||
|
|
||||||
if self:
|
if self:
|
||||||
objs = self
|
objs = self
|
||||||
else:
|
else:
|
||||||
objs = self.env['cleanup.purge.line.model']\
|
objs = self.env["cleanup.purge.line.model"].browse(
|
||||||
.browse(self._context.get('active_ids'))
|
self._context.get("active_ids")
|
||||||
|
)
|
||||||
for line in objs:
|
for line in objs:
|
||||||
self.env.cr.execute(
|
self.env.cr.execute(
|
||||||
"SELECT id, model from ir_model WHERE model = %s",
|
"SELECT id, model from ir_model WHERE model = %s", (line.name,)
|
||||||
(line.name,))
|
)
|
||||||
row = self.env.cr.fetchone()
|
row = self.env.cr.fetchone()
|
||||||
if not row:
|
if not row:
|
||||||
continue
|
continue
|
||||||
self.logger.info('Purging model %s', row[1])
|
self.logger.info("Purging model %s", row[1])
|
||||||
attachments = self.env['ir.attachment'].search([
|
attachments = self.env["ir.attachment"].search(
|
||||||
('res_model', '=', line.name)
|
[("res_model", "=", line.name)]
|
||||||
])
|
)
|
||||||
if attachments:
|
if attachments:
|
||||||
self.env.cr.execute(
|
self.env.cr.execute(
|
||||||
"UPDATE ir_attachment SET res_model = NULL "
|
"UPDATE ir_attachment SET res_model = NULL " "WHERE id in %s",
|
||||||
"WHERE id in %s",
|
(tuple(attachments.ids),),
|
||||||
(tuple(attachments.ids), ))
|
)
|
||||||
self.env['ir.model.constraint'].search([
|
self.env["ir.model.constraint"].search(
|
||||||
('model', '=', line.name),
|
[
|
||||||
]).unlink()
|
("model", "=", line.name),
|
||||||
relations = self.env['ir.model.fields'].search([
|
]
|
||||||
('relation', '=', row[1]),
|
).unlink()
|
||||||
]).with_context(**context_flags)
|
relations = (
|
||||||
|
self.env["ir.model.fields"]
|
||||||
|
.search(
|
||||||
|
[
|
||||||
|
("relation", "=", row[1]),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
.with_context(**context_flags)
|
||||||
|
)
|
||||||
for relation in relations:
|
for relation in relations:
|
||||||
try:
|
try:
|
||||||
# Fails if the model on the target side
|
# Fails if the model on the target side
|
||||||
|
@ -85,19 +96,18 @@ class CleanupPurgeLineModel(models.TransientModel):
|
||||||
pass
|
pass
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
pass
|
pass
|
||||||
self.env['ir.model.relation'].search([
|
self.env["ir.model.relation"].search(
|
||||||
('model', '=', line.name)
|
[("model", "=", line.name)]
|
||||||
]).with_context(**context_flags).unlink()
|
).with_context(**context_flags).unlink()
|
||||||
self.env['ir.model'].browse([row[0]])\
|
self.env["ir.model"].browse([row[0]]).with_context(**context_flags).unlink()
|
||||||
.with_context(**context_flags).unlink()
|
line.write({"purged": True})
|
||||||
line.write({'purged': True})
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
class CleanupPurgeWizardModel(models.TransientModel):
|
class CleanupPurgeWizardModel(models.TransientModel):
|
||||||
_inherit = 'cleanup.purge.wizard'
|
_inherit = "cleanup.purge.wizard"
|
||||||
_name = 'cleanup.purge.wizard.model'
|
_name = "cleanup.purge.wizard.model"
|
||||||
_description = 'Purge models'
|
_description = "Purge models"
|
||||||
|
|
||||||
@api.model
|
@api.model
|
||||||
def find(self):
|
def find(self):
|
||||||
|
@ -106,12 +116,13 @@ class CleanupPurgeWizardModel(models.TransientModel):
|
||||||
"""
|
"""
|
||||||
res = []
|
res = []
|
||||||
self.env.cr.execute("SELECT model from ir_model")
|
self.env.cr.execute("SELECT model from ir_model")
|
||||||
for model, in self.env.cr.fetchall():
|
for (model,) in self.env.cr.fetchall():
|
||||||
if model not in self.env:
|
if model not in self.env:
|
||||||
res.append((0, 0, {'name': model}))
|
res.append((0, 0, {"name": model}))
|
||||||
if not res:
|
if not res:
|
||||||
raise UserError(_('No orphaned models found'))
|
raise UserError(_("No orphaned models found"))
|
||||||
return res
|
return res
|
||||||
|
|
||||||
purge_line_ids = fields.One2many(
|
purge_line_ids = fields.One2many(
|
||||||
'cleanup.purge.line.model', 'wizard_id', 'Models to purge')
|
"cleanup.purge.line.model", "wizard_id", "Models to purge"
|
||||||
|
)
|
||||||
|
|
|
@ -4,35 +4,39 @@
|
||||||
from odoo import _, api, fields, models
|
from odoo import _, api, fields, models
|
||||||
from odoo.exceptions import UserError
|
from odoo.exceptions import UserError
|
||||||
from odoo.modules.module import get_module_path
|
from odoo.modules.module import get_module_path
|
||||||
|
|
||||||
from odoo.addons.base.models.ir_model import MODULE_UNINSTALL_FLAG
|
from odoo.addons.base.models.ir_model import MODULE_UNINSTALL_FLAG
|
||||||
|
|
||||||
|
|
||||||
class IrModelData(models.Model):
|
class IrModelData(models.Model):
|
||||||
_inherit = 'ir.model.data'
|
_inherit = "ir.model.data"
|
||||||
|
|
||||||
@api.model
|
@api.model
|
||||||
def _module_data_uninstall(self, modules_to_remove):
|
def _module_data_uninstall(self, modules_to_remove):
|
||||||
"""this function crashes for xmlids on undefined models or fields
|
"""this function crashes for xmlids on undefined models or fields
|
||||||
referring to undefined models"""
|
referring to undefined models"""
|
||||||
for this in self.search([('module', 'in', modules_to_remove)]):
|
for this in self.search([("module", "in", modules_to_remove)]):
|
||||||
if this.model == 'ir.model.fields':
|
if this.model == "ir.model.fields":
|
||||||
field = self.env[this.model].with_context(
|
field = (
|
||||||
**{MODULE_UNINSTALL_FLAG: True}).browse(this.res_id)
|
self.env[this.model]
|
||||||
|
.with_context(**{MODULE_UNINSTALL_FLAG: True})
|
||||||
|
.browse(this.res_id)
|
||||||
|
)
|
||||||
if not field.exists() or field.model not in self.env:
|
if not field.exists() or field.model not in self.env:
|
||||||
this.unlink()
|
this.unlink()
|
||||||
continue
|
continue
|
||||||
if this.model not in self.env:
|
if this.model not in self.env:
|
||||||
this.unlink()
|
this.unlink()
|
||||||
return super(IrModelData, self)._module_data_uninstall(
|
return super(IrModelData, self)._module_data_uninstall(modules_to_remove)
|
||||||
modules_to_remove)
|
|
||||||
|
|
||||||
|
|
||||||
class CleanupPurgeLineModule(models.TransientModel):
|
class CleanupPurgeLineModule(models.TransientModel):
|
||||||
_inherit = 'cleanup.purge.line'
|
_inherit = "cleanup.purge.line"
|
||||||
_name = 'cleanup.purge.line.module'
|
_name = "cleanup.purge.line.module"
|
||||||
|
|
||||||
wizard_id = fields.Many2one(
|
wizard_id = fields.Many2one(
|
||||||
'cleanup.purge.wizard.module', 'Purge Wizard', readonly=True)
|
"cleanup.purge.wizard.module", "Purge Wizard", readonly=True
|
||||||
|
)
|
||||||
|
|
||||||
@api.multi
|
@api.multi
|
||||||
def purge(self):
|
def purge(self):
|
||||||
|
@ -40,48 +44,46 @@ class CleanupPurgeLineModule(models.TransientModel):
|
||||||
Uninstall modules upon manual confirmation, then reload
|
Uninstall modules upon manual confirmation, then reload
|
||||||
the database.
|
the database.
|
||||||
"""
|
"""
|
||||||
module_names = self.filtered(lambda x: not x.purged).mapped('name')
|
module_names = self.filtered(lambda x: not x.purged).mapped("name")
|
||||||
modules = self.env['ir.module.module'].search([
|
modules = self.env["ir.module.module"].search([("name", "in", module_names)])
|
||||||
('name', 'in', module_names)
|
|
||||||
])
|
|
||||||
if not modules:
|
if not modules:
|
||||||
return True
|
return True
|
||||||
self.logger.info('Purging modules %s', ', '.join(module_names))
|
self.logger.info("Purging modules %s", ", ".join(module_names))
|
||||||
modules.filtered(
|
modules.filtered(
|
||||||
lambda x: x.state not in ('uninstallable', 'uninstalled')
|
lambda x: x.state not in ("uninstallable", "uninstalled")
|
||||||
).button_immediate_uninstall()
|
).button_immediate_uninstall()
|
||||||
modules.refresh()
|
modules.refresh()
|
||||||
modules.unlink()
|
modules.unlink()
|
||||||
return self.write({'purged': True})
|
return self.write({"purged": True})
|
||||||
|
|
||||||
|
|
||||||
class CleanupPurgeWizardModule(models.TransientModel):
|
class CleanupPurgeWizardModule(models.TransientModel):
|
||||||
_inherit = 'cleanup.purge.wizard'
|
_inherit = "cleanup.purge.wizard"
|
||||||
_name = 'cleanup.purge.wizard.module'
|
_name = "cleanup.purge.wizard.module"
|
||||||
_description = 'Purge modules'
|
_description = "Purge modules"
|
||||||
|
|
||||||
@api.model
|
@api.model
|
||||||
def find(self):
|
def find(self):
|
||||||
res = []
|
res = []
|
||||||
IrModule = self.env['ir.module.module']
|
IrModule = self.env["ir.module.module"]
|
||||||
for module in IrModule.search(
|
for module in IrModule.search(
|
||||||
[
|
[("to_buy", "=", False), ("name", "!=", "studio_customization")]
|
||||||
('to_buy', '=', False),
|
|
||||||
('name', '!=', 'studio_customization')
|
|
||||||
]
|
|
||||||
):
|
):
|
||||||
if get_module_path(module.name, display_warning=False):
|
if get_module_path(module.name, display_warning=False):
|
||||||
continue
|
continue
|
||||||
if module.state == 'uninstalled':
|
if module.state == "uninstalled":
|
||||||
self.env['cleanup.purge.line.module'].create({
|
self.env["cleanup.purge.line.module"].create(
|
||||||
'name': module.name,
|
{
|
||||||
}).purge()
|
"name": module.name,
|
||||||
|
}
|
||||||
|
).purge()
|
||||||
continue
|
continue
|
||||||
res.append((0, 0, {'name': module.name}))
|
res.append((0, 0, {"name": module.name}))
|
||||||
|
|
||||||
if not res:
|
if not res:
|
||||||
raise UserError(_('No modules found to purge'))
|
raise UserError(_("No modules found to purge"))
|
||||||
return res
|
return res
|
||||||
|
|
||||||
purge_line_ids = fields.One2many(
|
purge_line_ids = fields.One2many(
|
||||||
'cleanup.purge.line.module', 'wizard_id', 'Modules to purge')
|
"cleanup.purge.line.module", "wizard_id", "Modules to purge"
|
||||||
|
)
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
# Copyright 2017 Therp BV <http://therp.nl>
|
# Copyright 2017 Therp BV <http://therp.nl>
|
||||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||||
# pylint: disable=consider-merging-classes-inherited
|
# pylint: disable=consider-merging-classes-inherited
|
||||||
from odoo import api, models, fields
|
from odoo import api, fields, models
|
||||||
|
|
||||||
REASON_DUPLICATE = 1
|
REASON_DUPLICATE = 1
|
||||||
REASON_DEFAULT = 2
|
REASON_DEFAULT = 2
|
||||||
REASON_DEFAULT_FALSE = 3
|
REASON_DEFAULT_FALSE = 3
|
||||||
|
@ -9,31 +10,34 @@ REASON_UNKNOWN_MODEL = 4
|
||||||
|
|
||||||
|
|
||||||
class CleanupPurgeLineProperty(models.TransientModel):
|
class CleanupPurgeLineProperty(models.TransientModel):
|
||||||
_inherit = 'cleanup.purge.line'
|
_inherit = "cleanup.purge.line"
|
||||||
_name = 'cleanup.purge.line.property'
|
_name = "cleanup.purge.line.property"
|
||||||
_description = 'Purge properties'
|
_description = "Purge properties"
|
||||||
|
|
||||||
wizard_id = fields.Many2one(
|
wizard_id = fields.Many2one(
|
||||||
'cleanup.purge.wizard.property', 'Purge Wizard', readonly=True)
|
"cleanup.purge.wizard.property", "Purge Wizard", readonly=True
|
||||||
property_id = fields.Many2one('ir.property')
|
)
|
||||||
reason = fields.Selection([
|
property_id = fields.Many2one("ir.property")
|
||||||
(REASON_DUPLICATE, 'Duplicated property'),
|
reason = fields.Selection(
|
||||||
(REASON_DEFAULT, 'Same value as default'),
|
[
|
||||||
(REASON_DEFAULT_FALSE, 'Empty default property'),
|
(REASON_DUPLICATE, "Duplicated property"),
|
||||||
(REASON_UNKNOWN_MODEL, 'Unknown model'),
|
(REASON_DEFAULT, "Same value as default"),
|
||||||
])
|
(REASON_DEFAULT_FALSE, "Empty default property"),
|
||||||
|
(REASON_UNKNOWN_MODEL, "Unknown model"),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
@api.multi
|
@api.multi
|
||||||
def purge(self):
|
def purge(self):
|
||||||
"""Delete properties"""
|
"""Delete properties"""
|
||||||
self.write({'purged': True})
|
self.write({"purged": True})
|
||||||
return self.mapped('property_id').unlink()
|
return self.mapped("property_id").unlink()
|
||||||
|
|
||||||
|
|
||||||
class CleanupPurgeWizardProperty(models.TransientModel):
|
class CleanupPurgeWizardProperty(models.TransientModel):
|
||||||
_inherit = 'cleanup.purge.wizard'
|
_inherit = "cleanup.purge.wizard"
|
||||||
_name = 'cleanup.purge.wizard.property'
|
_name = "cleanup.purge.wizard.property"
|
||||||
_description = 'Purge properties'
|
_description = "Purge properties"
|
||||||
|
|
||||||
@api.model
|
@api.model
|
||||||
def find(self):
|
def find(self):
|
||||||
|
@ -41,96 +45,118 @@ class CleanupPurgeWizardProperty(models.TransientModel):
|
||||||
Search property records which are duplicated or the same as the default
|
Search property records which are duplicated or the same as the default
|
||||||
"""
|
"""
|
||||||
result = []
|
result = []
|
||||||
default_properties = self.env['ir.property'].search([
|
default_properties = self.env["ir.property"].search(
|
||||||
('res_id', '=', False),
|
[
|
||||||
])
|
("res_id", "=", False),
|
||||||
|
]
|
||||||
|
)
|
||||||
handled_field_ids = []
|
handled_field_ids = []
|
||||||
for prop in default_properties:
|
for prop in default_properties:
|
||||||
value = None
|
value = None
|
||||||
try:
|
try:
|
||||||
value = prop.get_by_record()
|
value = prop.get_by_record()
|
||||||
except KeyError:
|
except KeyError:
|
||||||
result.append({
|
result.append(
|
||||||
'name': '%s@%s: %s' % (
|
{
|
||||||
prop.name, prop.res_id, value,
|
"name": "%s@%s: %s"
|
||||||
),
|
% (
|
||||||
'property_id': prop.id,
|
prop.name,
|
||||||
'reason': REASON_UNKNOWN_MODEL,
|
prop.res_id,
|
||||||
})
|
value,
|
||||||
|
),
|
||||||
|
"property_id": prop.id,
|
||||||
|
"reason": REASON_UNKNOWN_MODEL,
|
||||||
|
}
|
||||||
|
)
|
||||||
continue
|
continue
|
||||||
if not value:
|
if not value:
|
||||||
result.append({
|
result.append(
|
||||||
'name': '%s@%s: %s' % (
|
{
|
||||||
prop.name, prop.res_id, value,
|
"name": "%s@%s: %s"
|
||||||
),
|
% (
|
||||||
'property_id': prop.id,
|
prop.name,
|
||||||
'reason': REASON_DEFAULT_FALSE,
|
prop.res_id,
|
||||||
})
|
value,
|
||||||
|
),
|
||||||
|
"property_id": prop.id,
|
||||||
|
"reason": REASON_DEFAULT_FALSE,
|
||||||
|
}
|
||||||
|
)
|
||||||
continue
|
continue
|
||||||
if prop.fields_id.id in handled_field_ids:
|
if prop.fields_id.id in handled_field_ids:
|
||||||
continue
|
continue
|
||||||
domain = [
|
domain = [
|
||||||
('id', '!=', prop.id),
|
("id", "!=", prop.id),
|
||||||
('fields_id', '=', prop.fields_id.id),
|
("fields_id", "=", prop.fields_id.id),
|
||||||
# =? explicitly tests for None or False, not falsyness
|
# =? explicitly tests for None or False, not falsyness
|
||||||
('value_float', '=?', prop.value_float or False),
|
("value_float", "=?", prop.value_float or False),
|
||||||
('value_integer', '=?', prop.value_integer or False),
|
("value_integer", "=?", prop.value_integer or False),
|
||||||
('value_text', '=?', prop.value_text or False),
|
("value_text", "=?", prop.value_text or False),
|
||||||
('value_binary', '=?', prop.value_binary or False),
|
("value_binary", "=?", prop.value_binary or False),
|
||||||
('value_reference', '=?', prop.value_reference or False),
|
("value_reference", "=?", prop.value_reference or False),
|
||||||
('value_datetime', '=?', prop.value_datetime or False),
|
("value_datetime", "=?", prop.value_datetime or False),
|
||||||
]
|
]
|
||||||
if prop.company_id:
|
if prop.company_id:
|
||||||
domain.append(('company_id', '=', prop.company_id.id))
|
domain.append(("company_id", "=", prop.company_id.id))
|
||||||
else:
|
else:
|
||||||
domain.extend([
|
domain.extend(
|
||||||
'|',
|
[
|
||||||
('company_id', '=', False),
|
"|",
|
||||||
(
|
("company_id", "=", False),
|
||||||
'company_id', 'in', self.env['res.company'].search([
|
(
|
||||||
(
|
"company_id",
|
||||||
'id', 'not in', default_properties.filtered(
|
"in",
|
||||||
lambda x: x.company_id and
|
self.env["res.company"]
|
||||||
x.fields_id == prop.fields_id
|
.search(
|
||||||
).ids,
|
[
|
||||||
|
(
|
||||||
|
"id",
|
||||||
|
"not in",
|
||||||
|
default_properties.filtered(
|
||||||
|
lambda x: x.company_id
|
||||||
|
and x.fields_id == prop.fields_id
|
||||||
|
).ids,
|
||||||
|
)
|
||||||
|
]
|
||||||
)
|
)
|
||||||
]).ids
|
.ids,
|
||||||
),
|
),
|
||||||
])
|
]
|
||||||
|
)
|
||||||
|
|
||||||
for redundant_property in self.env['ir.property'].search(domain):
|
for redundant_property in self.env["ir.property"].search(domain):
|
||||||
result.append({
|
result.append(
|
||||||
'name': '%s@%s: %s' % (
|
{
|
||||||
prop.name, redundant_property.res_id,
|
"name": "%s@%s: %s"
|
||||||
prop.get_by_record()
|
% (prop.name, redundant_property.res_id, prop.get_by_record()),
|
||||||
),
|
"property_id": redundant_property.id,
|
||||||
'property_id': redundant_property.id,
|
"reason": REASON_DEFAULT,
|
||||||
'reason': REASON_DEFAULT,
|
}
|
||||||
})
|
)
|
||||||
handled_field_ids.append(prop.fields_id.id)
|
handled_field_ids.append(prop.fields_id.id)
|
||||||
self.env.cr.execute(
|
self.env.cr.execute(
|
||||||
'''
|
"""
|
||||||
with grouped_properties(ids, cnt) as (
|
with grouped_properties(ids, cnt) as (
|
||||||
select array_agg(id), count(*)
|
select array_agg(id), count(*)
|
||||||
from ir_property group by res_id, company_id, fields_id
|
from ir_property group by res_id, company_id, fields_id
|
||||||
)
|
)
|
||||||
select ids from grouped_properties where cnt > 1
|
select ids from grouped_properties where cnt > 1
|
||||||
'''
|
"""
|
||||||
)
|
)
|
||||||
for ids, in self.env.cr.fetchall():
|
for (ids,) in self.env.cr.fetchall():
|
||||||
# odoo uses the first property found by search
|
# odoo uses the first property found by search
|
||||||
for prop in self.env['ir.property'].search([
|
for prop in self.env["ir.property"].search([("id", "in", ids)])[1:]:
|
||||||
('id', 'in', ids)
|
result.append(
|
||||||
])[1:]:
|
{
|
||||||
result.append({
|
"name": "%s@%s: %s"
|
||||||
'name': '%s@%s: %s' % (
|
% (prop.name, prop.res_id, prop.get_by_record()),
|
||||||
prop.name, prop.res_id, prop.get_by_record()
|
"property_id": prop.id,
|
||||||
),
|
"reason": REASON_DUPLICATE,
|
||||||
'property_id': prop.id,
|
}
|
||||||
'reason': REASON_DUPLICATE,
|
)
|
||||||
})
|
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
purge_line_ids = fields.One2many(
|
purge_line_ids = fields.One2many(
|
||||||
'cleanup.purge.line.property', 'wizard_id', 'Properties to purge')
|
"cleanup.purge.line.property", "wizard_id", "Properties to purge"
|
||||||
|
)
|
||||||
|
|
|
@ -1,18 +1,20 @@
|
||||||
# Copyright 2014-2016 Therp BV <http://therp.nl>
|
# Copyright 2014-2016 Therp BV <http://therp.nl>
|
||||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||||
# pylint: disable=consider-merging-classes-inherited
|
# pylint: disable=consider-merging-classes-inherited
|
||||||
from odoo import api, fields, models, _
|
from odoo import _, api, fields, models
|
||||||
from odoo.exceptions import UserError
|
from odoo.exceptions import UserError
|
||||||
|
|
||||||
from ..identifier_adapter import IdentifierAdapter
|
from ..identifier_adapter import IdentifierAdapter
|
||||||
|
|
||||||
|
|
||||||
class CleanupPurgeLineTable(models.TransientModel):
|
class CleanupPurgeLineTable(models.TransientModel):
|
||||||
_inherit = 'cleanup.purge.line'
|
_inherit = "cleanup.purge.line"
|
||||||
_name = 'cleanup.purge.line.table'
|
_name = "cleanup.purge.line.table"
|
||||||
_description = 'Purge tables wizard lines'
|
_description = "Purge tables wizard lines"
|
||||||
|
|
||||||
wizard_id = fields.Many2one(
|
wizard_id = fields.Many2one(
|
||||||
'cleanup.purge.wizard.table', 'Purge Wizard', readonly=True)
|
"cleanup.purge.wizard.table", "Purge Wizard", readonly=True
|
||||||
|
)
|
||||||
|
|
||||||
@api.multi
|
@api.multi
|
||||||
def purge(self):
|
def purge(self):
|
||||||
|
@ -22,9 +24,10 @@ class CleanupPurgeLineTable(models.TransientModel):
|
||||||
if self:
|
if self:
|
||||||
objs = self
|
objs = self
|
||||||
else:
|
else:
|
||||||
objs = self.env['cleanup.purge.line.table']\
|
objs = self.env["cleanup.purge.line.table"].browse(
|
||||||
.browse(self._context.get('active_ids'))
|
self._context.get("active_ids")
|
||||||
tables = objs.mapped('name')
|
)
|
||||||
|
tables = objs.mapped("name")
|
||||||
for line in objs:
|
for line in objs:
|
||||||
if line.purged:
|
if line.purged:
|
||||||
continue
|
continue
|
||||||
|
@ -49,32 +52,35 @@ class CleanupPurgeLineTable(models.TransientModel):
|
||||||
WHERE af.attnum = confkey AND af.attrelid = confrelid AND
|
WHERE af.attnum = confkey AND af.attrelid = confrelid AND
|
||||||
a.attnum = conkey AND a.attrelid = conrelid
|
a.attnum = conkey AND a.attrelid = conrelid
|
||||||
AND confrelid::regclass = '%s'::regclass;
|
AND confrelid::regclass = '%s'::regclass;
|
||||||
""", (IdentifierAdapter(line.name, quote=False),))
|
""",
|
||||||
|
(IdentifierAdapter(line.name, quote=False),),
|
||||||
|
)
|
||||||
|
|
||||||
for constraint in self.env.cr.fetchall():
|
for constraint in self.env.cr.fetchall():
|
||||||
if constraint[3] in tables:
|
if constraint[3] in tables:
|
||||||
self.logger.info(
|
self.logger.info(
|
||||||
'Dropping constraint %s on table %s (to be dropped)',
|
"Dropping constraint %s on table %s (to be dropped)",
|
||||||
constraint[0], constraint[3])
|
constraint[0],
|
||||||
|
constraint[3],
|
||||||
|
)
|
||||||
self.env.cr.execute(
|
self.env.cr.execute(
|
||||||
"ALTER TABLE %s DROP CONSTRAINT %s",
|
"ALTER TABLE %s DROP CONSTRAINT %s",
|
||||||
(
|
(
|
||||||
IdentifierAdapter(constraint[3]),
|
IdentifierAdapter(constraint[3]),
|
||||||
IdentifierAdapter(constraint[0])
|
IdentifierAdapter(constraint[0]),
|
||||||
))
|
),
|
||||||
|
)
|
||||||
|
|
||||||
self.logger.info(
|
self.logger.info("Dropping table %s", line.name)
|
||||||
'Dropping table %s', line.name)
|
self.env.cr.execute("DROP TABLE %s", (IdentifierAdapter(line.name),))
|
||||||
self.env.cr.execute(
|
line.write({"purged": True})
|
||||||
"DROP TABLE %s", (IdentifierAdapter(line.name),))
|
|
||||||
line.write({'purged': True})
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
class CleanupPurgeWizardTable(models.TransientModel):
|
class CleanupPurgeWizardTable(models.TransientModel):
|
||||||
_inherit = 'cleanup.purge.wizard'
|
_inherit = "cleanup.purge.wizard"
|
||||||
_name = 'cleanup.purge.wizard.table'
|
_name = "cleanup.purge.wizard.table"
|
||||||
_description = 'Purge tables'
|
_description = "Purge tables"
|
||||||
|
|
||||||
@api.model
|
@api.model
|
||||||
def find(self):
|
def find(self):
|
||||||
|
@ -83,7 +89,7 @@ class CleanupPurgeWizardTable(models.TransientModel):
|
||||||
Ignore views for now.
|
Ignore views for now.
|
||||||
"""
|
"""
|
||||||
known_tables = []
|
known_tables = []
|
||||||
for model in self.env['ir.model'].search([]):
|
for model in self.env["ir.model"].search([]):
|
||||||
if model.model not in self.env:
|
if model.model not in self.env:
|
||||||
continue
|
continue
|
||||||
model_pool = self.env[model.model]
|
model_pool = self.env[model.model]
|
||||||
|
@ -91,8 +97,8 @@ class CleanupPurgeWizardTable(models.TransientModel):
|
||||||
known_tables += [
|
known_tables += [
|
||||||
column.relation
|
column.relation
|
||||||
for column in model_pool._fields.values()
|
for column in model_pool._fields.values()
|
||||||
if column.type == 'many2many' and
|
if column.type == "many2many"
|
||||||
(column.compute is None or column.store)
|
and (column.compute is None or column.store)
|
||||||
and column.relation
|
and column.relation
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -100,12 +106,15 @@ class CleanupPurgeWizardTable(models.TransientModel):
|
||||||
"""
|
"""
|
||||||
SELECT table_name FROM information_schema.tables
|
SELECT table_name FROM information_schema.tables
|
||||||
WHERE table_schema = 'public' AND table_type = 'BASE TABLE'
|
WHERE table_schema = 'public' AND table_type = 'BASE TABLE'
|
||||||
AND table_name NOT IN %s""", (tuple(known_tables),))
|
AND table_name NOT IN %s""",
|
||||||
|
(tuple(known_tables),),
|
||||||
|
)
|
||||||
|
|
||||||
res = [(0, 0, {'name': row[0]}) for row in self.env.cr.fetchall()]
|
res = [(0, 0, {"name": row[0]}) for row in self.env.cr.fetchall()]
|
||||||
if not res:
|
if not res:
|
||||||
raise UserError(_('No orphaned tables found'))
|
raise UserError(_("No orphaned tables found"))
|
||||||
return res
|
return res
|
||||||
|
|
||||||
purge_line_ids = fields.One2many(
|
purge_line_ids = fields.One2many(
|
||||||
'cleanup.purge.line.table', 'wizard_id', 'Tables to purge')
|
"cleanup.purge.line.table", "wizard_id", "Tables to purge"
|
||||||
|
)
|
||||||
|
|
|
@ -3,21 +3,23 @@
|
||||||
# pylint: disable=consider-merging-classes-inherited
|
# pylint: disable=consider-merging-classes-inherited
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from odoo import _, api, fields, models
|
from odoo import _, api, fields, models
|
||||||
from odoo.exceptions import AccessDenied
|
from odoo.exceptions import AccessDenied
|
||||||
|
|
||||||
|
|
||||||
class CleanupPurgeLine(models.AbstractModel):
|
class CleanupPurgeLine(models.AbstractModel):
|
||||||
""" Abstract base class for the purge wizard lines """
|
""" Abstract base class for the purge wizard lines """
|
||||||
_name = 'cleanup.purge.line'
|
|
||||||
_order = 'name'
|
|
||||||
_description = 'Purge Column Abstract Wizard'
|
|
||||||
|
|
||||||
name = fields.Char('Name', readonly=True)
|
_name = "cleanup.purge.line"
|
||||||
purged = fields.Boolean('Purged', readonly=True)
|
_order = "name"
|
||||||
wizard_id = fields.Many2one('cleanup.purge.wizard')
|
_description = "Purge Column Abstract Wizard"
|
||||||
|
|
||||||
logger = logging.getLogger('odoo.addons.database_cleanup')
|
name = fields.Char("Name", readonly=True)
|
||||||
|
purged = fields.Boolean("Purged", readonly=True)
|
||||||
|
wizard_id = fields.Many2one("cleanup.purge.wizard")
|
||||||
|
|
||||||
|
logger = logging.getLogger("odoo.addons.database_cleanup")
|
||||||
|
|
||||||
@api.multi
|
@api.multi
|
||||||
def purge(self):
|
def purge(self):
|
||||||
|
@ -26,22 +28,22 @@ class CleanupPurgeLine(models.AbstractModel):
|
||||||
@api.model
|
@api.model
|
||||||
def create(self, values):
|
def create(self, values):
|
||||||
# make sure the user trying this is actually supposed to do it
|
# make sure the user trying this is actually supposed to do it
|
||||||
if self.env.ref(
|
if self.env.ref("base.group_erp_manager") not in self.env.user.groups_id:
|
||||||
'base.group_erp_manager') not in self.env.user.groups_id:
|
|
||||||
raise AccessDenied
|
raise AccessDenied
|
||||||
return super(CleanupPurgeLine, self).create(values)
|
return super(CleanupPurgeLine, self).create(values)
|
||||||
|
|
||||||
|
|
||||||
class PurgeWizard(models.AbstractModel):
|
class PurgeWizard(models.AbstractModel):
|
||||||
""" Abstract base class for the purge wizards """
|
""" Abstract base class for the purge wizards """
|
||||||
_name = 'cleanup.purge.wizard'
|
|
||||||
_description = 'Purge stuff'
|
_name = "cleanup.purge.wizard"
|
||||||
|
_description = "Purge stuff"
|
||||||
|
|
||||||
@api.model
|
@api.model
|
||||||
def default_get(self, fields_list):
|
def default_get(self, fields_list):
|
||||||
res = super(PurgeWizard, self).default_get(fields_list)
|
res = super(PurgeWizard, self).default_get(fields_list)
|
||||||
if 'purge_line_ids' in fields_list:
|
if "purge_line_ids" in fields_list:
|
||||||
res['purge_line_ids'] = self.find()
|
res["purge_line_ids"] = self.find()
|
||||||
return res
|
return res
|
||||||
|
|
||||||
@api.multi
|
@api.multi
|
||||||
|
@ -50,47 +52,43 @@ class PurgeWizard(models.AbstractModel):
|
||||||
|
|
||||||
@api.multi
|
@api.multi
|
||||||
def purge_all(self):
|
def purge_all(self):
|
||||||
self.mapped('purge_line_ids').purge()
|
self.mapped("purge_line_ids").purge()
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@api.model
|
@api.model
|
||||||
def get_wizard_action(self):
|
def get_wizard_action(self):
|
||||||
wizard = self.create({})
|
wizard = self.create({})
|
||||||
return {
|
return {
|
||||||
'type': 'ir.actions.act_window',
|
"type": "ir.actions.act_window",
|
||||||
'name': wizard.display_name,
|
"name": wizard.display_name,
|
||||||
'views': [(False, 'form')],
|
"views": [(False, "form")],
|
||||||
'res_model': self._name,
|
"res_model": self._name,
|
||||||
'res_id': wizard.id,
|
"res_id": wizard.id,
|
||||||
'flags': {
|
"flags": {
|
||||||
'action_buttons': False,
|
"action_buttons": False,
|
||||||
'sidebar': False,
|
"sidebar": False,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@api.multi
|
@api.multi
|
||||||
def select_lines(self):
|
def select_lines(self):
|
||||||
return {
|
return {
|
||||||
'type': 'ir.actions.act_window',
|
"type": "ir.actions.act_window",
|
||||||
'name': _('Select lines to purge'),
|
"name": _("Select lines to purge"),
|
||||||
'views': [(False, 'tree'), (False, 'form')],
|
"views": [(False, "tree"), (False, "form")],
|
||||||
'res_model': self._fields['purge_line_ids'].comodel_name,
|
"res_model": self._fields["purge_line_ids"].comodel_name,
|
||||||
'domain': [('wizard_id', 'in', self.ids)],
|
"domain": [("wizard_id", "in", self.ids)],
|
||||||
}
|
}
|
||||||
|
|
||||||
@api.multi
|
@api.multi
|
||||||
def name_get(self):
|
def name_get(self):
|
||||||
return [
|
return [(this.id, self._description) for this in self]
|
||||||
(this.id, self._description)
|
|
||||||
for this in self
|
|
||||||
]
|
|
||||||
|
|
||||||
@api.model
|
@api.model
|
||||||
def create(self, values):
|
def create(self, values):
|
||||||
# make sure the user trying this is actually supposed to do it
|
# make sure the user trying this is actually supposed to do it
|
||||||
if self.env.ref(
|
if self.env.ref("base.group_erp_manager") not in self.env.user.groups_id:
|
||||||
'base.group_erp_manager') not in self.env.user.groups_id:
|
|
||||||
raise AccessDenied
|
raise AccessDenied
|
||||||
return super(PurgeWizard, self).create(values)
|
return super(PurgeWizard, self).create(values)
|
||||||
|
|
||||||
purge_line_ids = fields.One2many('cleanup.purge.line', 'wizard_id')
|
purge_line_ids = fields.One2many("cleanup.purge.line", "wizard_id")
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
# Copyright 2016 Therp BV <http://therp.nl>
|
# Copyright 2016 Therp BV <http://therp.nl>
|
||||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||||
from psycopg2 import ProgrammingError
|
from psycopg2 import ProgrammingError
|
||||||
|
|
||||||
from odoo.modules.registry import Registry
|
from odoo.modules.registry import Registry
|
||||||
from odoo.tools import config, mute_logger
|
|
||||||
from odoo.tests.common import TransactionCase, at_install, post_install
|
from odoo.tests.common import TransactionCase, at_install, post_install
|
||||||
|
from odoo.tools import config, mute_logger
|
||||||
|
|
||||||
|
|
||||||
# Use post_install to get all models loaded more info: odoo/odoo#13458
|
# Use post_install to get all models loaded more info: odoo/odoo#13458
|
||||||
|
@ -15,103 +16,129 @@ class TestDatabaseCleanup(TransactionCase):
|
||||||
self.module = None
|
self.module = None
|
||||||
self.model = None
|
self.model = None
|
||||||
# Create one property for tests
|
# Create one property for tests
|
||||||
self.env['ir.property'].create({
|
self.env["ir.property"].create(
|
||||||
'fields_id': self.env.ref('base.field_res_partner__name').id,
|
{
|
||||||
'type': 'char',
|
"fields_id": self.env.ref("base.field_res_partner__name").id,
|
||||||
'value_text': 'My default partner name',
|
"type": "char",
|
||||||
})
|
"value_text": "My default partner name",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
def test_database_cleanup(self):
|
def test_database_cleanup(self):
|
||||||
# delete some index and check if our module recreated it
|
# delete some index and check if our module recreated it
|
||||||
self.env.cr.execute('drop index res_partner_name_index')
|
self.env.cr.execute("drop index res_partner_name_index")
|
||||||
create_indexes = self.env['cleanup.create_indexes.wizard'].create({})
|
create_indexes = self.env["cleanup.create_indexes.wizard"].create({})
|
||||||
create_indexes.purge_all()
|
create_indexes.purge_all()
|
||||||
self.env.cr.execute(
|
self.env.cr.execute(
|
||||||
'select indexname from pg_indexes '
|
"select indexname from pg_indexes "
|
||||||
"where indexname='res_partner_name_index' and "
|
"where indexname='res_partner_name_index' and "
|
||||||
"tablename='res_partner'"
|
"tablename='res_partner'"
|
||||||
)
|
)
|
||||||
self.assertEqual(self.env.cr.rowcount, 1)
|
self.assertEqual(self.env.cr.rowcount, 1)
|
||||||
# duplicate a property
|
# duplicate a property
|
||||||
duplicate_property = self.env['ir.property'].search([], limit=1).copy()
|
duplicate_property = self.env["ir.property"].search([], limit=1).copy()
|
||||||
purge_property = self.env['cleanup.purge.wizard.property'].create({})
|
purge_property = self.env["cleanup.purge.wizard.property"].create({})
|
||||||
purge_property.purge_all()
|
purge_property.purge_all()
|
||||||
self.assertFalse(duplicate_property.exists())
|
self.assertFalse(duplicate_property.exists())
|
||||||
# create an orphaned column
|
# create an orphaned column
|
||||||
self.env.cr.execute(
|
self.env.cr.execute(
|
||||||
'alter table res_partner add column database_cleanup_test int')
|
"alter table res_partner add column database_cleanup_test int"
|
||||||
|
)
|
||||||
# We need use a model that is not blocked (Avoid use res.users)
|
# We need use a model that is not blocked (Avoid use res.users)
|
||||||
partner_model = self.env['ir.model'].search([
|
partner_model = self.env["ir.model"].search(
|
||||||
('model', '=', 'res.partner')], limit=1)
|
[("model", "=", "res.partner")], limit=1
|
||||||
purge_columns = self.env['cleanup.purge.wizard.column'].create({
|
)
|
||||||
'purge_line_ids': [(0, 0, {
|
purge_columns = self.env["cleanup.purge.wizard.column"].create(
|
||||||
'model_id': partner_model.id, 'name': 'database_cleanup_test'}
|
{
|
||||||
)]})
|
"purge_line_ids": [
|
||||||
|
(
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
{"model_id": partner_model.id, "name": "database_cleanup_test"},
|
||||||
|
)
|
||||||
|
]
|
||||||
|
}
|
||||||
|
)
|
||||||
purge_columns.purge_all()
|
purge_columns.purge_all()
|
||||||
# must be removed by the wizard
|
# must be removed by the wizard
|
||||||
with self.assertRaises(ProgrammingError):
|
with self.assertRaises(ProgrammingError):
|
||||||
with self.env.registry.cursor() as cr:
|
with self.env.registry.cursor() as cr:
|
||||||
with mute_logger('odoo.sql_db'):
|
with mute_logger("odoo.sql_db"):
|
||||||
cr.execute('select database_cleanup_test from res_partner')
|
cr.execute("select database_cleanup_test from res_partner")
|
||||||
|
|
||||||
# create a data entry pointing nowhere
|
# create a data entry pointing nowhere
|
||||||
self.env.cr.execute('select max(id) + 1 from res_users')
|
self.env.cr.execute("select max(id) + 1 from res_users")
|
||||||
self.env['ir.model.data'].create({
|
self.env["ir.model.data"].create(
|
||||||
'module': 'database_cleanup',
|
{
|
||||||
'name': 'test_no_data_entry',
|
"module": "database_cleanup",
|
||||||
'model': 'res.users',
|
"name": "test_no_data_entry",
|
||||||
'res_id': self.env.cr.fetchone()[0],
|
"model": "res.users",
|
||||||
})
|
"res_id": self.env.cr.fetchone()[0],
|
||||||
purge_data = self.env['cleanup.purge.wizard.data'].create({})
|
}
|
||||||
|
)
|
||||||
|
purge_data = self.env["cleanup.purge.wizard.data"].create({})
|
||||||
purge_data.purge_all()
|
purge_data.purge_all()
|
||||||
# must be removed by the wizard
|
# must be removed by the wizard
|
||||||
with self.assertRaises(ValueError):
|
with self.assertRaises(ValueError):
|
||||||
self.env.ref('database_cleanup.test_no_data_entry')
|
self.env.ref("database_cleanup.test_no_data_entry")
|
||||||
|
|
||||||
# create a nonexistent model
|
# create a nonexistent model
|
||||||
self.model = self.env['ir.model'].create({
|
self.model = self.env["ir.model"].create(
|
||||||
'name': 'Database cleanup test model',
|
{
|
||||||
'model': 'x_database.cleanup.test.model',
|
"name": "Database cleanup test model",
|
||||||
})
|
"model": "x_database.cleanup.test.model",
|
||||||
|
}
|
||||||
|
)
|
||||||
self.env.cr.execute(
|
self.env.cr.execute(
|
||||||
'insert into ir_attachment (name, res_model, res_id, type) values '
|
"insert into ir_attachment (name, res_model, res_id, type) values "
|
||||||
"('test attachment', 'database.cleanup.test.model', 42, 'binary')")
|
"('test attachment', 'database.cleanup.test.model', 42, 'binary')"
|
||||||
self.env.registry.models.pop('x_database.cleanup.test.model')
|
)
|
||||||
purge_models = self.env['cleanup.purge.wizard.model'].create({})
|
self.env.registry.models.pop("x_database.cleanup.test.model")
|
||||||
|
purge_models = self.env["cleanup.purge.wizard.model"].create({})
|
||||||
purge_models.purge_all()
|
purge_models.purge_all()
|
||||||
# must be removed by the wizard
|
# must be removed by the wizard
|
||||||
self.assertFalse(self.env['ir.model'].search([
|
self.assertFalse(
|
||||||
('model', '=', 'x_database.cleanup.test.model'),
|
self.env["ir.model"].search(
|
||||||
]))
|
[
|
||||||
|
("model", "=", "x_database.cleanup.test.model"),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
# create a nonexistent module
|
# create a nonexistent module
|
||||||
self.module = self.env['ir.module.module'].create({
|
self.module = self.env["ir.module.module"].create(
|
||||||
'name': 'database_cleanup_test',
|
{
|
||||||
'state': 'to upgrade',
|
"name": "database_cleanup_test",
|
||||||
})
|
"state": "to upgrade",
|
||||||
purge_modules = self.env['cleanup.purge.wizard.module'].create({})
|
}
|
||||||
|
)
|
||||||
|
purge_modules = self.env["cleanup.purge.wizard.module"].create({})
|
||||||
# this reloads our registry, and we don't want to run tests twice
|
# this reloads our registry, and we don't want to run tests twice
|
||||||
# we also need the original registry for further tests, so save a
|
# we also need the original registry for further tests, so save a
|
||||||
# reference to it
|
# reference to it
|
||||||
original_registry = Registry.registries[self.env.cr.dbname]
|
original_registry = Registry.registries[self.env.cr.dbname]
|
||||||
config.options['test_enable'] = False
|
config.options["test_enable"] = False
|
||||||
purge_modules.purge_all()
|
purge_modules.purge_all()
|
||||||
config.options['test_enable'] = True
|
config.options["test_enable"] = True
|
||||||
# must be removed by the wizard
|
# must be removed by the wizard
|
||||||
self.assertFalse(self.env['ir.module.module'].search([
|
self.assertFalse(
|
||||||
('name', '=', 'database_cleanup_test'),
|
self.env["ir.module.module"].search(
|
||||||
]))
|
[
|
||||||
|
("name", "=", "database_cleanup_test"),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
)
|
||||||
# reset afterwards
|
# reset afterwards
|
||||||
Registry.registries[self.env.cr.dbname] = original_registry
|
Registry.registries[self.env.cr.dbname] = original_registry
|
||||||
|
|
||||||
# create an orphaned table
|
# create an orphaned table
|
||||||
self.env.cr.execute('create table database_cleanup_test (test int)')
|
self.env.cr.execute("create table database_cleanup_test (test int)")
|
||||||
purge_tables = self.env['cleanup.purge.wizard.table'].create({})
|
purge_tables = self.env["cleanup.purge.wizard.table"].create({})
|
||||||
purge_tables.purge_all()
|
purge_tables.purge_all()
|
||||||
with self.assertRaises(ProgrammingError):
|
with self.assertRaises(ProgrammingError):
|
||||||
with self.env.registry.cursor() as cr:
|
with self.env.registry.cursor() as cr:
|
||||||
with mute_logger('odoo.sql_db'):
|
with mute_logger("odoo.sql_db"):
|
||||||
cr.execute('select * from database_cleanup_test')
|
cr.execute("select * from database_cleanup_test")
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
super(TestDatabaseCleanup, self).tearDown()
|
super(TestDatabaseCleanup, self).tearDown()
|
||||||
|
@ -120,10 +147,8 @@ class TestDatabaseCleanup(TransactionCase):
|
||||||
self.env.cr.rollback()
|
self.env.cr.rollback()
|
||||||
if self.module:
|
if self.module:
|
||||||
cr2.execute(
|
cr2.execute(
|
||||||
"DELETE FROM ir_module_module WHERE id=%s",
|
"DELETE FROM ir_module_module WHERE id=%s", (self.module.id,)
|
||||||
(self.module.id,))
|
)
|
||||||
if self.model:
|
if self.model:
|
||||||
cr2.execute(
|
cr2.execute("DELETE FROM ir_model WHERE id=%s", (self.model.id,))
|
||||||
"DELETE FROM ir_model WHERE id=%s",
|
|
||||||
(self.model.id,))
|
|
||||||
cr2.commit()
|
cr2.commit()
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
<odoo>
|
<odoo>
|
||||||
<record id="cleanup_create_indexes_wizard_view_form" model="ir.ui.view">
|
<record id="cleanup_create_indexes_wizard_view_form" model="ir.ui.view">
|
||||||
<field name="model">cleanup.create_indexes.wizard</field>
|
<field name="model">cleanup.create_indexes.wizard</field>
|
||||||
|
@ -18,8 +18,13 @@
|
||||||
<field name="name">Create missing indexes</field>
|
<field name="name">Create missing indexes</field>
|
||||||
<field name="type">ir.actions.server</field>
|
<field name="type">ir.actions.server</field>
|
||||||
<field name="state">code</field>
|
<field name="state">code</field>
|
||||||
<field name="model_id" ref="database_cleanup.model_cleanup_create_indexes_wizard" />
|
<field
|
||||||
<field name="code">action = env.get('cleanup.create_indexes.wizard').get_wizard_action()</field>
|
name="model_id"
|
||||||
|
ref="database_cleanup.model_cleanup_create_indexes_wizard"
|
||||||
|
/>
|
||||||
|
<field
|
||||||
|
name="code"
|
||||||
|
>action = env.get('cleanup.create_indexes.wizard').get_wizard_action()</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
<record id="cleanup_create_indexes_line_view_tree" model="ir.ui.view">
|
<record id="cleanup_create_indexes_line_view_tree" model="ir.ui.view">
|
||||||
|
@ -38,8 +43,14 @@
|
||||||
<field name="name">Create</field>
|
<field name="name">Create</field>
|
||||||
<field name="type">ir.actions.server</field>
|
<field name="type">ir.actions.server</field>
|
||||||
<field name="state">code</field>
|
<field name="state">code</field>
|
||||||
<field name="model_id" ref="database_cleanup.model_cleanup_create_indexes_line" />
|
<field
|
||||||
|
name="model_id"
|
||||||
|
ref="database_cleanup.model_cleanup_create_indexes_line"
|
||||||
|
/>
|
||||||
<field name="code">records.purge()</field>
|
<field name="code">records.purge()</field>
|
||||||
<field name="binding_model_id" ref="database_cleanup.model_cleanup_create_indexes_line" />
|
<field
|
||||||
|
name="binding_model_id"
|
||||||
|
ref="database_cleanup.model_cleanup_create_indexes_line"
|
||||||
|
/>
|
||||||
</record>
|
</record>
|
||||||
</odoo>
|
</odoo>
|
||||||
|
|
|
@ -1,66 +1,66 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
<odoo>
|
<odoo>
|
||||||
<record model="ir.ui.menu" id="menu_database_cleanup">
|
<record model="ir.ui.menu" id="menu_database_cleanup">
|
||||||
<field name="name">Database cleanup</field>
|
<field name="name">Database cleanup</field>
|
||||||
<field name="sequence" eval="10" />
|
<field name="sequence" eval="10" />
|
||||||
<!-- attach to Settings -> Technical -->
|
<!-- attach to Settings -> Technical -->
|
||||||
<field name="parent_id" ref="base.menu_custom"/>
|
<field name="parent_id" ref="base.menu_custom" />
|
||||||
<field name="groups_id" eval="[(6,0, [ref('base.group_erp_manager')])]"/>
|
<field name="groups_id" eval="[(6,0, [ref('base.group_erp_manager')])]" />
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
<record model="ir.ui.menu" id="menu_purge_modules">
|
<record model="ir.ui.menu" id="menu_purge_modules">
|
||||||
<field name="name">Purge obsolete modules</field>
|
<field name="name">Purge obsolete modules</field>
|
||||||
<field name="sequence" eval="10" />
|
<field name="sequence" eval="10" />
|
||||||
<field name="action" ref="action_purge_modules" />
|
<field name="action" ref="action_purge_modules" />
|
||||||
<field name="parent_id" ref="menu_database_cleanup"/>
|
<field name="parent_id" ref="menu_database_cleanup" />
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
<record model="ir.ui.menu" id="menu_purge_models">
|
<record model="ir.ui.menu" id="menu_purge_models">
|
||||||
<field name="name">Purge obsolete models</field>
|
<field name="name">Purge obsolete models</field>
|
||||||
<field name="sequence" eval="20" />
|
<field name="sequence" eval="20" />
|
||||||
<field name="action" ref="action_purge_models" />
|
<field name="action" ref="action_purge_models" />
|
||||||
<field name="parent_id" ref="menu_database_cleanup"/>
|
<field name="parent_id" ref="menu_database_cleanup" />
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
<record model="ir.ui.menu" id="menu_purge_columns">
|
<record model="ir.ui.menu" id="menu_purge_columns">
|
||||||
<field name="name">Purge obsolete columns</field>
|
<field name="name">Purge obsolete columns</field>
|
||||||
<field name="sequence" eval="30" />
|
<field name="sequence" eval="30" />
|
||||||
<field name="action" ref="action_purge_columns" />
|
<field name="action" ref="action_purge_columns" />
|
||||||
<field name="parent_id" ref="menu_database_cleanup"/>
|
<field name="parent_id" ref="menu_database_cleanup" />
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
<record model="ir.ui.menu" id="menu_purge_tables">
|
<record model="ir.ui.menu" id="menu_purge_tables">
|
||||||
<field name="name">Purge obsolete tables</field>
|
<field name="name">Purge obsolete tables</field>
|
||||||
<field name="sequence" eval="40" />
|
<field name="sequence" eval="40" />
|
||||||
<field name="action" ref="action_purge_tables" />
|
<field name="action" ref="action_purge_tables" />
|
||||||
<field name="parent_id" ref="menu_database_cleanup"/>
|
<field name="parent_id" ref="menu_database_cleanup" />
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
<record model="ir.ui.menu" id="menu_purge_data">
|
<record model="ir.ui.menu" id="menu_purge_data">
|
||||||
<field name="name">Purge obsolete data entries</field>
|
<field name="name">Purge obsolete data entries</field>
|
||||||
<field name="sequence" eval="50" />
|
<field name="sequence" eval="50" />
|
||||||
<field name="action" ref="action_purge_data" />
|
<field name="action" ref="action_purge_data" />
|
||||||
<field name="parent_id" ref="menu_database_cleanup"/>
|
<field name="parent_id" ref="menu_database_cleanup" />
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
<record model="ir.ui.menu" id="menu_purge_menus">
|
<record model="ir.ui.menu" id="menu_purge_menus">
|
||||||
<field name="name">Purge obsolete menu entries</field>
|
<field name="name">Purge obsolete menu entries</field>
|
||||||
<field name="sequence" eval="60" />
|
<field name="sequence" eval="60" />
|
||||||
<field name="action" ref="action_purge_menus" />
|
<field name="action" ref="action_purge_menus" />
|
||||||
<field name="parent_id" ref="menu_database_cleanup"/>
|
<field name="parent_id" ref="menu_database_cleanup" />
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
<record model="ir.ui.menu" id="menu_create_indexes">
|
<record model="ir.ui.menu" id="menu_create_indexes">
|
||||||
<field name="name">Create missing indexes</field>
|
<field name="name">Create missing indexes</field>
|
||||||
<field name="sequence" eval="70" />
|
<field name="sequence" eval="70" />
|
||||||
<field name="action" ref="cleanup_create_indexes_wizard_action" />
|
<field name="action" ref="cleanup_create_indexes_wizard_action" />
|
||||||
<field name="parent_id" ref="menu_database_cleanup"/>
|
<field name="parent_id" ref="menu_database_cleanup" />
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
<record model="ir.ui.menu" id="menu_purge_property">
|
<record model="ir.ui.menu" id="menu_purge_property">
|
||||||
<field name="name">Purge obsolete properties</field>
|
<field name="name">Purge obsolete properties</field>
|
||||||
<field name="sequence" eval="80" />
|
<field name="sequence" eval="80" />
|
||||||
<field name="action" ref="action_purge_property" />
|
<field name="action" ref="action_purge_property" />
|
||||||
<field name="parent_id" ref="menu_database_cleanup"/>
|
<field name="parent_id" ref="menu_database_cleanup" />
|
||||||
</record>
|
</record>
|
||||||
</odoo>
|
</odoo>
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
<odoo>
|
<odoo>
|
||||||
<record id="purge_columns_view" model="ir.ui.view">
|
<record id="purge_columns_view" model="ir.ui.view">
|
||||||
<field name="model">cleanup.purge.wizard.column</field>
|
<field name="model">cleanup.purge.wizard.column</field>
|
||||||
|
@ -15,7 +15,10 @@
|
||||||
<field name="name">Purge columns</field>
|
<field name="name">Purge columns</field>
|
||||||
<field name="type">ir.actions.server</field>
|
<field name="type">ir.actions.server</field>
|
||||||
<field name="state">code</field>
|
<field name="state">code</field>
|
||||||
<field name="model_id" ref="database_cleanup.model_cleanup_purge_wizard_column" />
|
<field
|
||||||
|
name="model_id"
|
||||||
|
ref="database_cleanup.model_cleanup_purge_wizard_column"
|
||||||
|
/>
|
||||||
<field name="code">
|
<field name="code">
|
||||||
action = env.get('cleanup.purge.wizard.column').get_wizard_action()
|
action = env.get('cleanup.purge.wizard.column').get_wizard_action()
|
||||||
</field>
|
</field>
|
||||||
|
@ -38,6 +41,9 @@
|
||||||
<field name="state">code</field>
|
<field name="state">code</field>
|
||||||
<field name="model_id" ref="database_cleanup.model_cleanup_purge_line_column" />
|
<field name="model_id" ref="database_cleanup.model_cleanup_purge_line_column" />
|
||||||
<field name="code">records.purge()</field>
|
<field name="code">records.purge()</field>
|
||||||
<field name="binding_model_id" ref="database_cleanup.model_cleanup_purge_line_column" />
|
<field
|
||||||
|
name="binding_model_id"
|
||||||
|
ref="database_cleanup.model_cleanup_purge_line_column"
|
||||||
|
/>
|
||||||
</record>
|
</record>
|
||||||
</odoo>
|
</odoo>
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
<odoo>
|
<odoo>
|
||||||
<record id="purge_data_view" model="ir.ui.view">
|
<record id="purge_data_view" model="ir.ui.view">
|
||||||
<field name="model">cleanup.purge.wizard.data</field>
|
<field name="model">cleanup.purge.wizard.data</field>
|
||||||
|
@ -38,6 +38,9 @@
|
||||||
<field name="state">code</field>
|
<field name="state">code</field>
|
||||||
<field name="model_id" ref="database_cleanup.model_cleanup_purge_line_data" />
|
<field name="model_id" ref="database_cleanup.model_cleanup_purge_line_data" />
|
||||||
<field name="code">records.purge()</field>
|
<field name="code">records.purge()</field>
|
||||||
<field name="binding_model_id" ref="database_cleanup.model_cleanup_purge_line_data" />
|
<field
|
||||||
|
name="binding_model_id"
|
||||||
|
ref="database_cleanup.model_cleanup_purge_line_data"
|
||||||
|
/>
|
||||||
</record>
|
</record>
|
||||||
</odoo>
|
</odoo>
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
<odoo>
|
<odoo>
|
||||||
<record id="purge_menus_view" model="ir.ui.view">
|
<record id="purge_menus_view" model="ir.ui.view">
|
||||||
<field name="model">cleanup.purge.wizard.menu</field>
|
<field name="model">cleanup.purge.wizard.menu</field>
|
||||||
<field name="inherit_id" ref="form_purge_wizard" />
|
<field name="inherit_id" ref="form_purge_wizard" />
|
||||||
<field name="mode">primary</field>
|
<field name="mode">primary</field>
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<data/>
|
<data />
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
|
@ -34,6 +34,9 @@
|
||||||
<field name="state">code</field>
|
<field name="state">code</field>
|
||||||
<field name="model_id" ref="database_cleanup.model_cleanup_purge_line_menu" />
|
<field name="model_id" ref="database_cleanup.model_cleanup_purge_line_menu" />
|
||||||
<field name="code">records.purge()</field>
|
<field name="code">records.purge()</field>
|
||||||
<field name="binding_model_id" ref="database_cleanup.model_cleanup_purge_line_menu" />
|
<field
|
||||||
|
name="binding_model_id"
|
||||||
|
ref="database_cleanup.model_cleanup_purge_line_menu"
|
||||||
|
/>
|
||||||
</record>
|
</record>
|
||||||
</odoo>
|
</odoo>
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
<odoo>
|
<odoo>
|
||||||
<record id="purge_models_view" model="ir.ui.view">
|
<record id="purge_models_view" model="ir.ui.view">
|
||||||
<field name="model">cleanup.purge.wizard.model</field>
|
<field name="model">cleanup.purge.wizard.model</field>
|
||||||
|
@ -13,7 +13,10 @@
|
||||||
<field name="name">Purge models</field>
|
<field name="name">Purge models</field>
|
||||||
<field name="type">ir.actions.server</field>
|
<field name="type">ir.actions.server</field>
|
||||||
<field name="state">code</field>
|
<field name="state">code</field>
|
||||||
<field name="model_id" ref="database_cleanup.model_cleanup_purge_wizard_model" />
|
<field
|
||||||
|
name="model_id"
|
||||||
|
ref="database_cleanup.model_cleanup_purge_wizard_model"
|
||||||
|
/>
|
||||||
<field name="code">
|
<field name="code">
|
||||||
action = env.get('cleanup.purge.wizard.model').get_wizard_action()
|
action = env.get('cleanup.purge.wizard.model').get_wizard_action()
|
||||||
</field>
|
</field>
|
||||||
|
@ -34,6 +37,9 @@
|
||||||
<field name="state">code</field>
|
<field name="state">code</field>
|
||||||
<field name="model_id" ref="database_cleanup.model_cleanup_purge_line_model" />
|
<field name="model_id" ref="database_cleanup.model_cleanup_purge_line_model" />
|
||||||
<field name="code">records.purge()</field>
|
<field name="code">records.purge()</field>
|
||||||
<field name="binding_model_id" ref="database_cleanup.model_cleanup_purge_line_model" />
|
<field
|
||||||
|
name="binding_model_id"
|
||||||
|
ref="database_cleanup.model_cleanup_purge_line_model"
|
||||||
|
/>
|
||||||
</record>
|
</record>
|
||||||
</odoo>
|
</odoo>
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
<odoo>
|
<odoo>
|
||||||
<record id="purge_modules_view" model="ir.ui.view">
|
<record id="purge_modules_view" model="ir.ui.view">
|
||||||
<field name="model">cleanup.purge.wizard.module</field>
|
<field name="model">cleanup.purge.wizard.module</field>
|
||||||
|
@ -13,7 +13,10 @@
|
||||||
<field name="name">Purge modules</field>
|
<field name="name">Purge modules</field>
|
||||||
<field name="type">ir.actions.server</field>
|
<field name="type">ir.actions.server</field>
|
||||||
<field name="state">code</field>
|
<field name="state">code</field>
|
||||||
<field name="model_id" ref="database_cleanup.model_cleanup_purge_wizard_module" />
|
<field
|
||||||
|
name="model_id"
|
||||||
|
ref="database_cleanup.model_cleanup_purge_wizard_module"
|
||||||
|
/>
|
||||||
<field name="code">
|
<field name="code">
|
||||||
action = env.get('cleanup.purge.wizard.module').get_wizard_action()
|
action = env.get('cleanup.purge.wizard.module').get_wizard_action()
|
||||||
</field>
|
</field>
|
||||||
|
@ -24,7 +27,7 @@
|
||||||
<field name="inherit_id" ref="tree_purge_line" />
|
<field name="inherit_id" ref="tree_purge_line" />
|
||||||
<field name="mode">primary</field>
|
<field name="mode">primary</field>
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<data/>
|
<data />
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
|
@ -34,6 +37,9 @@
|
||||||
<field name="state">code</field>
|
<field name="state">code</field>
|
||||||
<field name="model_id" ref="database_cleanup.model_cleanup_purge_line_module" />
|
<field name="model_id" ref="database_cleanup.model_cleanup_purge_line_module" />
|
||||||
<field name="code">records.purge()</field>
|
<field name="code">records.purge()</field>
|
||||||
<field name="binding_model_id" ref="database_cleanup.model_cleanup_purge_line_module" />
|
<field
|
||||||
|
name="binding_model_id"
|
||||||
|
ref="database_cleanup.model_cleanup_purge_line_module"
|
||||||
|
/>
|
||||||
</record>
|
</record>
|
||||||
</odoo>
|
</odoo>
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
<odoo>
|
<odoo>
|
||||||
<record id="purge_property_view" model="ir.ui.view">
|
<record id="purge_property_view" model="ir.ui.view">
|
||||||
<field name="model">cleanup.purge.wizard.property</field>
|
<field name="model">cleanup.purge.wizard.property</field>
|
||||||
|
@ -13,8 +13,13 @@
|
||||||
<field name="name">Purge properties</field>
|
<field name="name">Purge properties</field>
|
||||||
<field name="type">ir.actions.server</field>
|
<field name="type">ir.actions.server</field>
|
||||||
<field name="state">code</field>
|
<field name="state">code</field>
|
||||||
<field name="model_id" ref="database_cleanup.model_cleanup_purge_wizard_property" />
|
<field
|
||||||
<field name="code">action = env.get('cleanup.purge.wizard.property').get_wizard_action()</field>
|
name="model_id"
|
||||||
|
ref="database_cleanup.model_cleanup_purge_wizard_property"
|
||||||
|
/>
|
||||||
|
<field
|
||||||
|
name="code"
|
||||||
|
>action = env.get('cleanup.purge.wizard.property').get_wizard_action()</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
<record id="purge_property_line_tree" model="ir.ui.view">
|
<record id="purge_property_line_tree" model="ir.ui.view">
|
||||||
|
@ -32,8 +37,14 @@
|
||||||
<field name="name">Purge</field>
|
<field name="name">Purge</field>
|
||||||
<field name="type">ir.actions.server</field>
|
<field name="type">ir.actions.server</field>
|
||||||
<field name="state">code</field>
|
<field name="state">code</field>
|
||||||
<field name="model_id" ref="database_cleanup.model_cleanup_purge_line_property" />
|
<field
|
||||||
|
name="model_id"
|
||||||
|
ref="database_cleanup.model_cleanup_purge_line_property"
|
||||||
|
/>
|
||||||
<field name="code">records.purge()</field>
|
<field name="code">records.purge()</field>
|
||||||
<field name="binding_model_id" ref="database_cleanup.model_cleanup_purge_line_property" />
|
<field
|
||||||
|
name="binding_model_id"
|
||||||
|
ref="database_cleanup.model_cleanup_purge_line_property"
|
||||||
|
/>
|
||||||
</record>
|
</record>
|
||||||
</odoo>
|
</odoo>
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
<odoo>
|
<odoo>
|
||||||
<record id="purge_tables_view" model="ir.ui.view">
|
<record id="purge_tables_view" model="ir.ui.view">
|
||||||
<field name="model">cleanup.purge.wizard.table</field>
|
<field name="model">cleanup.purge.wizard.table</field>
|
||||||
|
@ -13,7 +13,10 @@
|
||||||
<field name="name">Purge tables</field>
|
<field name="name">Purge tables</field>
|
||||||
<field name="type">ir.actions.server</field>
|
<field name="type">ir.actions.server</field>
|
||||||
<field name="state">code</field>
|
<field name="state">code</field>
|
||||||
<field name="model_id" ref="database_cleanup.model_cleanup_purge_wizard_table" />
|
<field
|
||||||
|
name="model_id"
|
||||||
|
ref="database_cleanup.model_cleanup_purge_wizard_table"
|
||||||
|
/>
|
||||||
<field name="code">
|
<field name="code">
|
||||||
action = env.get('cleanup.purge.wizard.table').get_wizard_action()
|
action = env.get('cleanup.purge.wizard.table').get_wizard_action()
|
||||||
</field>
|
</field>
|
||||||
|
@ -34,6 +37,9 @@
|
||||||
<field name="state">code</field>
|
<field name="state">code</field>
|
||||||
<field name="model_id" ref="database_cleanup.model_cleanup_purge_line_table" />
|
<field name="model_id" ref="database_cleanup.model_cleanup_purge_line_table" />
|
||||||
<field name="code">records.purge()</field>
|
<field name="code">records.purge()</field>
|
||||||
<field name="binding_model_id" ref="database_cleanup.model_cleanup_purge_line_table" />
|
<field
|
||||||
|
name="binding_model_id"
|
||||||
|
ref="database_cleanup.model_cleanup_purge_line_table"
|
||||||
|
/>
|
||||||
</record>
|
</record>
|
||||||
</odoo>
|
</odoo>
|
||||||
|
|
|
@ -1,26 +1,38 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
<odoo>
|
<odoo>
|
||||||
<record id="form_purge_wizard" model="ir.ui.view">
|
<record id="form_purge_wizard" model="ir.ui.view">
|
||||||
<field name="model">cleanup.purge.wizard</field>
|
<field name="model">cleanup.purge.wizard</field>
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<form>
|
<form>
|
||||||
<header>
|
<header>
|
||||||
<button type="object" name="purge_all" string="Purge all" class="oe_highlight" />
|
<button
|
||||||
|
type="object"
|
||||||
|
name="purge_all"
|
||||||
|
string="Purge all"
|
||||||
|
class="oe_highlight"
|
||||||
|
/>
|
||||||
<button type="object" name="select_lines" string="Select lines" />
|
<button type="object" name="select_lines" string="Select lines" />
|
||||||
</header>
|
</header>
|
||||||
<div attrs="{'invisible': [('purge_line_ids', '!=', [])]}">
|
<div attrs="{'invisible': [('purge_line_ids', '!=', [])]}">
|
||||||
Nothing found to clean up.
|
Nothing found to clean up.
|
||||||
</div>
|
</div>
|
||||||
<field name="purge_line_ids" attrs="{'invisible': [('purge_line_ids', '=', [])]}">
|
<field
|
||||||
|
name="purge_line_ids"
|
||||||
|
attrs="{'invisible': [('purge_line_ids', '=', [])]}"
|
||||||
|
>
|
||||||
<form>
|
<form>
|
||||||
<group>
|
<group>
|
||||||
<field name="name" />
|
<field name="name" />
|
||||||
<field name="purged" />
|
<field name="purged" />
|
||||||
</group>
|
</group>
|
||||||
<footer>
|
<footer>
|
||||||
<button type="object" name="purge" class="oe_highlight"
|
<button
|
||||||
string="Purge"
|
type="object"
|
||||||
attrs="{'invisible': [('purged', '=', True)]}"/>
|
name="purge"
|
||||||
|
class="oe_highlight"
|
||||||
|
string="Purge"
|
||||||
|
attrs="{'invisible': [('purged', '=', True)]}"
|
||||||
|
/>
|
||||||
</footer>
|
</footer>
|
||||||
</form>
|
</form>
|
||||||
</field>
|
</field>
|
||||||
|
@ -33,9 +45,13 @@
|
||||||
<tree string="Purge models" delete="false" create="false">
|
<tree string="Purge models" delete="false" create="false">
|
||||||
<field name="name" />
|
<field name="name" />
|
||||||
<field name="purged" />
|
<field name="purged" />
|
||||||
<button type="object" name="purge"
|
<button
|
||||||
icon="fa-times-circle text-danger" string="Purge this model"
|
type="object"
|
||||||
attrs="{'invisible': [('purged', '=', True)]}"/>
|
name="purge"
|
||||||
|
icon="fa-times-circle text-danger"
|
||||||
|
string="Purge this model"
|
||||||
|
attrs="{'invisible': [('purged', '=', True)]}"
|
||||||
|
/>
|
||||||
</tree>
|
</tree>
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
Loading…
Reference in New Issue