Yannick Vaucher
9 years ago
committed by
Artem Kostyuk
8 changed files with 214 additions and 316 deletions
-
71record_archiver/README.rst
-
88record_archiver/__openerp__.py
-
90record_archiver/models/ir_model.py
-
123record_archiver/models/record_lifespan.py
-
6record_archiver/tests/__init__.py
-
70record_archiver/tests/test_active_search.py
-
74record_archiver/tests/test_archive.py
-
8record_archiver/views/record_lifespan_view.xml
@ -0,0 +1,71 @@ |
|||||
|
.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg |
||||
|
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html |
||||
|
:alt: License: AGPL-3 |
||||
|
|
||||
|
================ |
||||
|
Records Archiver |
||||
|
================ |
||||
|
|
||||
|
Create a cron job that deactivates old records in order to optimize |
||||
|
performance. |
||||
|
|
||||
|
Records are deactivated based on their last activity (write_date). |
||||
|
|
||||
|
Configuration |
||||
|
============= |
||||
|
|
||||
|
You can configure lifespan of each type of record in |
||||
|
`Settings -> Configuration -> Records Archiver` |
||||
|
|
||||
|
A different lifespan can be configured for each model. |
||||
|
|
||||
|
Usage |
||||
|
===== |
||||
|
|
||||
|
Once the lifespans are configured, the cron will automatically |
||||
|
deactivate the old records. |
||||
|
|
||||
|
Known issues / Roadmap |
||||
|
====================== |
||||
|
|
||||
|
The default behavior is to archive all records having a ``write_date`` < |
||||
|
lifespan and with a state being ``done`` or ``cancel``. If these rules |
||||
|
need to be modified for a model (e.g. change the states to archive), the |
||||
|
hook ``RecordLifespan._archive_domain`` can be extended. |
||||
|
|
||||
|
Bug Tracker |
||||
|
=========== |
||||
|
|
||||
|
Bugs are tracked on `GitHub Issues |
||||
|
<https://github.com/OCA/server-tools/issues>`_. In case of trouble, please |
||||
|
check there if your issue has already been reported. If you spotted it first, |
||||
|
help us smashing it by providing a detailed and welcomed `feedback |
||||
|
<https://github.com/OCA/ |
||||
|
server-tools/issues/new?body=module:%20 |
||||
|
record_archiver%0Aversion:%20 |
||||
|
9.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_. |
||||
|
|
||||
|
|
||||
|
Credits |
||||
|
======= |
||||
|
|
||||
|
Contributors |
||||
|
------------ |
||||
|
|
||||
|
* Yannick Vaucher <yannick.vaucher@camptocamp.com> |
||||
|
* Guewen Baconnier <guewen.baconnier@camptocamp.com> |
||||
|
|
||||
|
Maintainer |
||||
|
---------- |
||||
|
|
||||
|
.. image:: https://odoo-community.org/logo.png |
||||
|
:alt: Odoo Community Association |
||||
|
:target: https://odoo-community.org |
||||
|
|
||||
|
This module is maintained by the OCA. |
||||
|
|
||||
|
OCA, or the Odoo Community Association, is a nonprofit organization whose |
||||
|
mission is to support the collaborative development of Odoo features and |
||||
|
promote its widespread use. |
||||
|
|
||||
|
To contribute to this module, please visit http://odoo-community.org. |
@ -1,73 +1,39 @@ |
|||||
# -*- coding: utf-8 -*- |
# -*- coding: utf-8 -*- |
||||
# |
|
||||
# |
|
||||
# Authors: Guewen Baconnier |
|
||||
# Copyright 2015 Camptocamp SA |
|
||||
# |
|
||||
# This program is free software: you can redistribute it and/or modify |
|
||||
# it under the terms of the GNU Affero General Public License as |
|
||||
# published by the Free Software Foundation, either version 3 of the |
|
||||
# License, or (at your option) any later version. |
|
||||
# |
|
||||
# This program is distributed in the hope that it will be useful, |
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
||||
# GNU Affero General Public License for more details. |
|
||||
# |
|
||||
# You should have received a copy of the GNU Affero General Public License |
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
||||
# |
|
||||
# |
|
||||
|
# © 2015 Guewen Baconnier (Camptocamp SA) |
||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). |
||||
|
from openerp import api, fields, models |
||||
|
|
||||
from openerp.osv import orm, fields |
|
||||
|
|
||||
|
|
||||
class IrModel(orm.Model): |
|
||||
|
class IrModel(models.Model): |
||||
_inherit = 'ir.model' |
_inherit = 'ir.model' |
||||
|
|
||||
def _compute_has_an_active_field(self, cr, uid, ids, name, |
|
||||
args, context=None): |
|
||||
res = {} |
|
||||
for model_id in ids: |
|
||||
active_field_ids = self.pool['ir.model.fields'].search( |
|
||||
cr, uid, |
|
||||
[('model_id', '=', model_id), |
|
||||
|
@api.multi |
||||
|
def _compute_has_an_active_field(self): |
||||
|
for model in self: |
||||
|
active_fields = self.env['ir.model.fields'].search( |
||||
|
[('model_id', '=', model.id), |
||||
('name', '=', 'active'), |
('name', '=', 'active'), |
||||
], |
], |
||||
limit=1, |
|
||||
context=context) |
|
||||
res[model_id] = bool(active_field_ids) |
|
||||
return res |
|
||||
|
limit=1) |
||||
|
model.has_an_active_field = bool(active_fields) |
||||
|
|
||||
def _search_has_an_active_field(self, cr, uid, obj, name, args, |
|
||||
context=None): |
|
||||
if not len(args): |
|
||||
return [] |
|
||||
fields_model = self.pool['ir.model.fields'] |
|
||||
|
@api.model |
||||
|
def _search_has_an_active_field(self, operator, value): |
||||
|
if operator not in ['=', '!=']: |
||||
|
raise AssertionError('operator %s not allowed' % operator) |
||||
|
fields_model = self.env['ir.model.fields'] |
||||
domain = [] |
domain = [] |
||||
for field, operator, value in args: |
|
||||
assert field == name |
|
||||
active_field_ids = fields_model.search( |
|
||||
cr, uid, [('name', '=', 'active')], context=context) |
|
||||
active_fields = fields_model.read(cr, uid, active_field_ids, |
|
||||
fields=['model_id'], |
|
||||
load='_classic_write', |
|
||||
context=context) |
|
||||
model_ids = [field['model_id'] for field in active_fields] |
|
||||
if operator == '=' or not value: |
|
||||
domain.append(('id', 'in', model_ids)) |
|
||||
elif operator == '!=' or value: |
|
||||
domain.append(('id', 'not in', model_ids)) |
|
||||
else: |
|
||||
raise AssertionError('operator %s not allowed' % operator) |
|
||||
|
active_fields = fields_model.search( |
||||
|
[('name', '=', 'active')]) |
||||
|
models = active_fields.mapped('model_id') |
||||
|
if operator == '=' and value or operator == '!=' and not value: |
||||
|
domain.append(('id', 'in', models.ids)) |
||||
|
else: |
||||
|
domain.append(('id', 'not in', models.ids)) |
||||
return domain |
return domain |
||||
|
|
||||
_columns = { |
|
||||
'has_an_active_field': fields.function( |
|
||||
_compute_has_an_active_field, |
|
||||
fnct_search=_search_has_an_active_field, |
|
||||
string='Has an active field', |
|
||||
readonly=True, |
|
||||
type='boolean', |
|
||||
), |
|
||||
} |
|
||||
|
has_an_active_field = fields.Boolean( |
||||
|
compute=_compute_has_an_active_field, |
||||
|
search=_search_has_an_active_field, |
||||
|
string='Has an active field', |
||||
|
) |
@ -1,56 +1,36 @@ |
|||||
# -*- coding: utf-8 -*- |
# -*- coding: utf-8 -*- |
||||
# |
|
||||
# |
|
||||
# Authors: Guewen Baconnier |
|
||||
# Copyright 2015 Camptocamp SA |
|
||||
# |
|
||||
# This program is free software: you can redistribute it and/or modify |
|
||||
# it under the terms of the GNU Affero General Public License as |
|
||||
# published by the Free Software Foundation, either version 3 of the |
|
||||
# License, or (at your option) any later version. |
|
||||
# |
|
||||
# This program is distributed in the hope that it will be useful, |
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
||||
# GNU Affero General Public License for more details. |
|
||||
# |
|
||||
# You should have received a copy of the GNU Affero General Public License |
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
||||
# |
|
||||
# |
|
||||
|
|
||||
|
|
||||
|
# © 2015 Guewen Baconnier (Camptocamp SA) |
||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). |
||||
import openerp.tests.common as common |
import openerp.tests.common as common |
||||
|
|
||||
|
|
||||
class TestActiveSearch(common.TransactionCase): |
class TestActiveSearch(common.TransactionCase): |
||||
|
|
||||
def test_model_with_active_field(self): |
def test_model_with_active_field(self): |
||||
cr, uid = self.cr, self.uid |
|
||||
IrModel = self.registry('ir.model') |
|
||||
partner_model_id = IrModel.search(cr, uid, |
|
||||
[('model', '=', 'res.partner')], |
|
||||
limit=1)[0] |
|
||||
partner_model = IrModel.browse(cr, uid, partner_model_id) |
|
||||
|
IrModel = self.env['ir.model'] |
||||
|
partner_model = IrModel.search([('model', '=', 'res.partner')], |
||||
|
limit=1) |
||||
self.assertTrue(partner_model.has_an_active_field) |
self.assertTrue(partner_model.has_an_active_field) |
||||
self.assertIn(partner_model_id, |
|
||||
IrModel.search(cr, uid, |
|
||||
[('has_an_active_field', '=', True)])) |
|
||||
self.assertIn(partner_model_id, |
|
||||
IrModel.search(cr, uid, |
|
||||
[('has_an_active_field', '!=', False)])) |
|
||||
|
self.assertIn(partner_model, |
||||
|
IrModel.search([('has_an_active_field', '=', True)])) |
||||
|
self.assertIn(partner_model, |
||||
|
IrModel.search([('has_an_active_field', '!=', False)])) |
||||
|
self.assertNotIn(partner_model, |
||||
|
IrModel.search([('has_an_active_field', '!=', True)])) |
||||
|
self.assertNotIn(partner_model, |
||||
|
IrModel.search([('has_an_active_field', '=', False)])) |
||||
|
|
||||
def test_model_without_active_field(self): |
def test_model_without_active_field(self): |
||||
cr, uid = self.cr, self.uid |
|
||||
IrModel = self.registry('ir.model') |
|
||||
country_model_id = IrModel.search(cr, uid, |
|
||||
[('model', '=', 'res.country')], |
|
||||
limit=1) |
|
||||
country_model = IrModel.browse(cr, uid, country_model_id[0]) |
|
||||
|
IrModel = self.env['ir.model'] |
||||
|
country_model = IrModel.search([('model', '=', 'res.country')], |
||||
|
limit=1) |
||||
self.assertFalse(country_model.has_an_active_field) |
self.assertFalse(country_model.has_an_active_field) |
||||
self.assertNotIn(country_model_id, |
|
||||
IrModel.search(cr, uid, |
|
||||
[('has_an_active_field', '=', False)])) |
|
||||
self.assertNotIn(country_model_id, |
|
||||
IrModel.search(cr, uid, |
|
||||
[('has_an_active_field', '!=', True)])) |
|
||||
|
self.assertIn(country_model, |
||||
|
IrModel.search([('has_an_active_field', '!=', True)])) |
||||
|
self.assertIn(country_model, |
||||
|
IrModel.search([('has_an_active_field', '=', False)])) |
||||
|
self.assertNotIn(country_model, |
||||
|
IrModel.search([('has_an_active_field', '=', True)])) |
||||
|
self.assertNotIn(country_model, |
||||
|
IrModel.search([('has_an_active_field', '!=', False)]) |
||||
|
) |
Write
Preview
Loading…
Cancel
Save
Reference in new issue